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,93 @@
1
+ import { BlockEditProps } from '@wordpress/blocks';
2
+ import { useBlockProps, InspectorControls, RichText } from '@wordpress/block-editor';
3
+ import { PanelBody, ToggleControl, SelectControl } from '@wordpress/components';
4
+ import { __ } from '@wordpress/i18n';
5
+ import { {{titleCase}}Attributes } from './types';
6
+ import { validators } from './validators';
7
+ import { useTypiaValidation, useAttributeLogger, useDebounce } from './hooks';
8
+ import { ErrorBoundary } from './components/ErrorBoundary';
9
+ import { classNames } from './utils';
10
+
11
+ type EditProps = BlockEditProps<{{titleCase}}Attributes>;
12
+
13
+ export default function Edit({ attributes, setAttributes }: EditProps) {
14
+ const blockProps = useBlockProps();
15
+ const { isValid, errors } = useTypiaValidation(attributes, validators.validate);
16
+ const debouncedAttributes = useDebounce(attributes, 300);
17
+
18
+ // Log attribute changes in development
19
+ useAttributeLogger(debouncedAttributes);
20
+
21
+ const updateAttribute = <K extends keyof {{titleCase}}Attributes>(
22
+ key: K,
23
+ value: {{titleCase}}Attributes[K]
24
+ ) => {
25
+ const newAttrs = { ...attributes, [key]: value };
26
+ const validation = validators.validate(newAttrs);
27
+
28
+ if (validation.success) {
29
+ setAttributes({ [key]: value } as Partial<{{titleCase}}Attributes>);
30
+ } else {
31
+ console.error(`Validation failed for ${String(key)}:`, validation.errors);
32
+ }
33
+ };
34
+
35
+ return (
36
+ <ErrorBoundary>
37
+ <InspectorControls>
38
+ <PanelBody title={__('Settings', '{{textdomain}}')}>
39
+ <ToggleControl
40
+ label={__('Visible', '{{textdomain}}')}
41
+ checked={attributes.isVisible ?? true}
42
+ onChange={(isVisible) => updateAttribute('isVisible', isVisible)}
43
+ />
44
+ <SelectControl
45
+ label={__('Alignment', '{{textdomain}}')}
46
+ value={attributes.alignment ?? 'left'}
47
+ options={[
48
+ { label: __('Left', '{{textdomain}}'), value: 'left' },
49
+ { label: __('Center', '{{textdomain}}'), value: 'center' },
50
+ { label: __('Right', '{{textdomain}}'), value: 'right' },
51
+ { label: __('Justify', '{{textdomain}}'), value: 'justify' },
52
+ ]}
53
+ onChange={(alignment) => updateAttribute('alignment', alignment as any)}
54
+ />
55
+ {!isValid && (
56
+ <div className="components-notice is-error">
57
+ <p><strong>{__('Validation Errors:', '{{textdomain}}')}</strong></p>
58
+ <ul style={{ margin: 0, paddingLeft: '1em' }}>
59
+ {errors.map((error, index) => (
60
+ <li key={index}>
61
+ <code>{error.path}</code>: {error.message}
62
+ </li>
63
+ ))}
64
+ </ul>
65
+ </div>
66
+ )}
67
+ </PanelBody>
68
+ </InspectorControls>
69
+
70
+ <div {...blockProps} className={classNames(blockProps.className, {
71
+ 'has-validation-errors': !isValid,
72
+ 'is-hidden': !attributes.isVisible
73
+ })}>
74
+ <RichText
75
+ tagName="p"
76
+ value={attributes.content}
77
+ onChange={(content) => updateAttribute('content', content)}
78
+ placeholder={__('Enter your text...', '{{textdomain}}')}
79
+ className={classNames('{{dashCase}}-content', `align-${attributes.alignment}`)}
80
+ style={{
81
+ textAlign: attributes.alignment ?? 'left'
82
+ }}
83
+ />
84
+
85
+ <div className="block-info">
86
+ <small>
87
+ {__('{{title}} – Enhanced with utilities, hooks, and error handling!', '{{textdomain}}')}
88
+ </small>
89
+ </div>
90
+ </div>
91
+ </ErrorBoundary>
92
+ );
93
+ }
@@ -0,0 +1,20 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ /**
4
+ * Hook for debounced value
5
+ */
6
+ export function useDebounce<T>(value: T, delay: number): T {
7
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
8
+
9
+ useEffect(() => {
10
+ const handler = setTimeout(() => {
11
+ setDebouncedValue(value);
12
+ }, delay);
13
+
14
+ return () => {
15
+ clearTimeout(handler);
16
+ };
17
+ }, [value, delay]);
18
+
19
+ return debouncedValue;
20
+ }
@@ -0,0 +1,31 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ /**
4
+ * Hook for localStorage with type safety
5
+ */
6
+ export function useLocalStorage<T>(
7
+ key: string,
8
+ initialValue: T
9
+ ): [T, (value: T | ((val: T) => T)) => void] {
10
+ const [storedValue, setStoredValue] = useState<T>(() => {
11
+ try {
12
+ const item = window.localStorage.getItem(key);
13
+ return item ? JSON.parse(item) : initialValue;
14
+ } catch (error) {
15
+ console.error(`Error reading localStorage key "${key}":`, error);
16
+ return initialValue;
17
+ }
18
+ });
19
+
20
+ const setValue = (value: T | ((val: T) => T)) => {
21
+ try {
22
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
23
+ setStoredValue(valueToStore);
24
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
25
+ } catch (error) {
26
+ console.error(`Error setting localStorage key "${key}":`, error);
27
+ }
28
+ };
29
+
30
+ return [storedValue, setValue];
31
+ }
@@ -0,0 +1,56 @@
1
+ import { useEffect, useState } from '@wordpress/element';
2
+
3
+ /**
4
+ * Custom hooks for {{title}}
5
+ */
6
+ export * from './hooks/useDebounce';
7
+ export * from './hooks/useLocalStorage';
8
+
9
+ /**
10
+ * Hook for Typia validation with real-time feedback
11
+ */
12
+ export function useTypiaValidation<T>(
13
+ data: T,
14
+ validator: (value: T) => { success: boolean; errors?: any[] }
15
+ ) {
16
+ const [isValid, setIsValid] = useState(true);
17
+ const [errors, setErrors] = useState<any[]>([]);
18
+
19
+ useEffect(() => {
20
+ const result = validator(data);
21
+ setIsValid(result.success);
22
+ setErrors(result.errors || []);
23
+ }, [data, validator]);
24
+
25
+ return { isValid, errors };
26
+ }
27
+
28
+ /**
29
+ * Hook for generating UUID
30
+ */
31
+ export function useUUID() {
32
+ const [uuid] = useState(() => {
33
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
34
+ return crypto.randomUUID();
35
+ }
36
+
37
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
38
+ const r = Math.random() * 16 | 0;
39
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
40
+ return v.toString(16);
41
+ });
42
+ });
43
+
44
+ return uuid;
45
+ }
46
+
47
+ /**
48
+ * Hook for logging attribute changes in development
49
+ */
50
+ export function useAttributeLogger(attributes: any) {
51
+ useEffect(() => {
52
+ if (process.env.NODE_ENV === 'development') {
53
+ console.log('{{title}} attributes changed:', attributes);
54
+ }
55
+ }, [attributes]);
56
+ }
@@ -0,0 +1,16 @@
1
+ import { registerBlockType } from '@wordpress/blocks';
2
+ import { __ } from '@wordpress/i18n';
3
+ import Edit from './edit';
4
+ import Save from './save';
5
+ import { {{titleCase}}Attributes } from './types';
6
+ import { validators } from './validators';
7
+ import './style.scss';
8
+
9
+ registerBlockType<{{titleCase}}Attributes>('{{namespace}}/{{slug}}', {
10
+ // Use Typia to generate example data
11
+ example: {
12
+ attributes: validators.random()
13
+ },
14
+ edit: Edit,
15
+ save: Save,
16
+ });
@@ -0,0 +1,417 @@
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
+
10
+ /**
11
+ * 🔍 WordPress Block Migration Detection System
12
+ *
13
+ * Automatically detects if blocks need migration and provides
14
+ * detailed analysis of required upgrades
15
+ */
16
+
17
+ export interface MigrationError extends Error {
18
+ name: string;
19
+ message: string;
20
+ stack?: string;
21
+ code: string;
22
+ details?: any;
23
+ timestamp: string;
24
+ blockData?: any;
25
+ }
26
+
27
+ export interface MigrationAnalysis {
28
+ needsMigration: boolean;
29
+ currentVersion: string;
30
+ targetVersion: string;
31
+ confidence: number; // 0-1
32
+ reasons: string[];
33
+ warnings: string[];
34
+ blockCount?: number;
35
+ affectedFields: {
36
+ added: string[];
37
+ modified: string[];
38
+ deprecated: string[];
39
+ };
40
+ }
41
+
42
+ export interface BlockScanResult {
43
+ blockName: string;
44
+ postId?: number;
45
+ postTitle?: string;
46
+ analysis: MigrationAnalysis;
47
+ attributes: any;
48
+ }
49
+
50
+ /**
51
+ * Detect if a single block needs migration
52
+ */
53
+ export function detectBlockMigration(attributes: any, blockName?: string): MigrationAnalysis {
54
+ const analysis: MigrationAnalysis = {
55
+ needsMigration: false,
56
+ currentVersion: "unknown",
57
+ targetVersion: "3.0.0",
58
+ confidence: 0,
59
+ reasons: [],
60
+ warnings: [],
61
+ affectedFields: {
62
+ added: [],
63
+ modified: [],
64
+ deprecated: []
65
+ }
66
+ };
67
+
68
+ try {
69
+ // 1. Structure-based detection
70
+ const structureDetection = detectVersionByStructure(attributes);
71
+ analysis.currentVersion = structureDetection.version;
72
+ analysis.confidence = structureDetection.confidence;
73
+
74
+ // 2. Schema validation against current version
75
+ const currentValidation = typia.validate<{{titleCase}}Attributes>(attributes);
76
+
77
+ if (!currentValidation.success) {
78
+ analysis.needsMigration = true;
79
+ analysis.reasons.push(`Block doesn't match current schema (v${analysis.targetVersion})`);
80
+
81
+ // Analyze validation errors to understand what's missing/wrong
82
+ analyzeValidationErrors(currentValidation.errors, analysis);
83
+ }
84
+
85
+ // 3. Version-specific checks
86
+ if (analysis.currentVersion !== analysis.targetVersion) {
87
+ analysis.needsMigration = true;
88
+ analysis.reasons.push(`Block is at v${analysis.currentVersion}, current is v${analysis.targetVersion}`);
89
+
90
+ // Compare schemas to find changes
91
+ compareVersionSchemas(analysis.currentVersion, analysis.targetVersion, analysis);
92
+ }
93
+
94
+ // 4. Data quality checks
95
+ performDataQualityChecks(attributes, analysis);
96
+
97
+ // 5. Add migration recommendations
98
+ if (analysis.needsMigration) {
99
+ addMigrationRecommendations(analysis);
100
+ }
101
+
102
+ } catch (error) {
103
+ // Enhanced error handling with detailed logging
104
+ const migrationError: MigrationError = {
105
+ name: error.name || 'MigrationDetectionError',
106
+ message: error.message || 'Unknown error during migration detection',
107
+ stack: error.stack,
108
+ code: 'MIGRATION_DETECTION_FAILED',
109
+ details: {
110
+ blockName,
111
+ attributes: typeof attributes === 'object' ? Object.keys(attributes) : 'Invalid attributes',
112
+ timestamp: new Date().toISOString()
113
+ },
114
+ timestamp: new Date().toISOString(),
115
+ blockData: attributes
116
+ };
117
+
118
+ analysis.warnings.push(`Detection error: ${migrationError.message} (Code: ${migrationError.code})`);
119
+ analysis.confidence = 0;
120
+
121
+ // Log detailed error for debugging
122
+ console.error('Migration Detection Error Details:', JSON.stringify(migrationError, null, 2));
123
+ }
124
+
125
+ return analysis;
126
+ }
127
+
128
+ /**
129
+ * Detect version based on block structure
130
+ */
131
+ function detectVersionByStructure(attributes: any): { version: string; confidence: number } {
132
+ const checks = [
133
+ // V3 indicators (highest priority)
134
+ {
135
+ version: "3.0.0",
136
+ indicators: ['id', 'version', 'theme'],
137
+ required: 2, // Need at least 2 indicators for high confidence
138
+ weight: 0.95
139
+ },
140
+ // V2 indicators (medium priority)
141
+ {
142
+ version: "2.0.0",
143
+ indicators: ['isVisible', 'className'],
144
+ required: 1,
145
+ weight: 0.85
146
+ },
147
+ // Mixed V2+V3 indicators
148
+ {
149
+ version: "2.5.0", // Intermediate version
150
+ indicators: ['isVisible', 'className', 'id', 'version'],
151
+ required: 3,
152
+ weight: 0.9
153
+ },
154
+ // V1 indicators (basic structure)
155
+ {
156
+ version: "1.0.0",
157
+ indicators: ['content', 'alignment'],
158
+ required: 1,
159
+ weight: 0.7
160
+ }
161
+ ];
162
+
163
+ let bestMatch = { version: "1.0.0", confidence: 0.3 }; // Default fallback
164
+
165
+ // Enhanced detection with weighted scoring
166
+ for (const check of checks) {
167
+ const presentIndicators = check.indicators.filter(indicator =>
168
+ indicator in attributes && attributes[indicator] !== undefined
169
+ );
170
+
171
+ if (presentIndicators.length >= check.required) {
172
+ // Calculate confidence based on proportion of indicators present and weight
173
+ const proportion = presentIndicators.length / check.indicators.length;
174
+ const confidence = proportion * check.weight;
175
+
176
+ // Bonus for exact match
177
+ const bonus = presentIndicators.length === check.indicators.length ? 0.1 : 0;
178
+
179
+ const finalConfidence = Math.min(confidence + bonus, 1.0);
180
+
181
+ if (finalConfidence > bestMatch.confidence) {
182
+ bestMatch = {
183
+ version: check.version,
184
+ confidence: finalConfidence
185
+ };
186
+ }
187
+ }
188
+ }
189
+
190
+ // Additional validation for V3 blocks
191
+ if (attributes.version && typeof attributes.version === 'number') {
192
+ if (attributes.version === 3 && bestMatch.version !== "3.0.0") {
193
+ // Override if version attribute explicitly states V3
194
+ bestMatch = { version: "3.0.0", confidence: Math.min(bestMatch.confidence + 0.2, 1.0) };
195
+ }
196
+ }
197
+
198
+ return bestMatch;
199
+ }
200
+
201
+ /**
202
+ * Analyze Typia validation errors to understand migration needs
203
+ */
204
+ function analyzeValidationErrors(errors: any[], analysis: MigrationAnalysis) {
205
+ for (const error of errors) {
206
+ const path = error.path || 'root';
207
+
208
+ if (error.expected && error.actual) {
209
+ analysis.affectedFields.modified.push(path);
210
+ analysis.reasons.push(`Field '${path}' type mismatch: expected ${error.expected}, got ${error.actual}`);
211
+ } else if (error.expected) {
212
+ analysis.affectedFields.added.push(path);
213
+ analysis.reasons.push(`Missing required field: '${path}'`);
214
+ }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Compare schemas between versions
220
+ */
221
+ function compareVersionSchemas(fromVersion: string, toVersion: string, analysis: MigrationAnalysis) {
222
+ // This would be auto-generated based on type analysis
223
+ const versionChanges = {
224
+ "1.0.0->2.0.0": {
225
+ added: ['isVisible', 'className'],
226
+ modified: ['content'], // Added length validation
227
+ deprecated: [],
228
+ changes: [
229
+ "Added visibility toggle (isVisible) for showing/hiding blocks",
230
+ "Added custom CSS class support (className) for styling",
231
+ "Enhanced content validation with length constraints"
232
+ ]
233
+ },
234
+ "2.0.0->3.0.0": {
235
+ added: ['id', 'version', 'theme'],
236
+ modified: ['content', 'className'], // Changed validation rules
237
+ deprecated: [],
238
+ changes: [
239
+ "Added unique ID generation for block identification",
240
+ "Added version tracking for future migrations",
241
+ "Added theme support for different visual styles",
242
+ "Enhanced content validation with increased max length (2000 chars)",
243
+ "Enhanced className validation with pattern matching"
244
+ ]
245
+ },
246
+ "1.0.0->3.0.0": {
247
+ added: ['id', 'version', 'theme', 'isVisible', 'className'],
248
+ modified: ['content', 'alignment'],
249
+ deprecated: [],
250
+ changes: [
251
+ "Added visibility toggle (isVisible) for showing/hiding blocks",
252
+ "Added custom CSS class support (className) for styling",
253
+ "Added unique ID generation for block identification",
254
+ "Added version tracking for future migrations",
255
+ "Added theme support for different visual styles",
256
+ "Enhanced content validation with length constraints and increased max length",
257
+ "Extended alignment options to include 'justify'"
258
+ ]
259
+ }
260
+ };
261
+
262
+ const changeKey = `${fromVersion}->${toVersion}`;
263
+ const changes = versionChanges[changeKey];
264
+
265
+ if (changes) {
266
+ analysis.affectedFields.added.push(...changes.added);
267
+ analysis.affectedFields.modified.push(...changes.modified);
268
+ analysis.affectedFields.deprecated.push(...changes.deprecated);
269
+
270
+ // Add change descriptions to reasons
271
+ if (changes.changes) {
272
+ analysis.reasons.push(...changes.changes.map(change => `📝 ${change}`));
273
+ }
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Perform data quality checks
279
+ */
280
+ function performDataQualityChecks(attributes: any, analysis: MigrationAnalysis) {
281
+ // Check for suspicious data patterns
282
+ if (attributes.content === null) {
283
+ analysis.warnings.push('Content is null, may cause issues after migration');
284
+ }
285
+
286
+ if (typeof attributes.content === 'object') {
287
+ analysis.warnings.push('Content appears to be an object, expected string');
288
+ }
289
+
290
+ if (attributes.className && attributes.className.length > 100) {
291
+ analysis.warnings.push('ClassName exceeds recommended length, will be truncated');
292
+ }
293
+
294
+ // Check for legacy WordPress patterns
295
+ if (attributes.content && typeof attributes.content === 'string') {
296
+ if (attributes.content.includes('{{')) {
297
+ analysis.warnings.push('Content contains template syntax, may need manual review');
298
+ }
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Add migration recommendations
304
+ */
305
+ function addMigrationRecommendations(analysis: MigrationAnalysis) {
306
+ if (analysis.currentVersion === "1.0.0") {
307
+ analysis.reasons.push('💡 Upgrade will add visibility toggle and custom CSS classes');
308
+ }
309
+
310
+ if (analysis.currentVersion !== "3.0.0") {
311
+ analysis.reasons.push('💡 Latest version includes unique IDs and theme support');
312
+ }
313
+
314
+ if (analysis.affectedFields.added.length > 0) {
315
+ analysis.reasons.push(`🔧 ${analysis.affectedFields.added.length} new field(s) will be added with default values`);
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Scan WordPress database for blocks needing migration
321
+ */
322
+ export async function scanSiteForMigrations(blockName: string = '{{namespace}}/{{slug}}'): Promise<BlockScanResult[]> {
323
+ console.log(`🔍 Scanning site for ${blockName} blocks needing migration...`);
324
+
325
+ const results: BlockScanResult[] = [];
326
+
327
+ try {
328
+ // This would integrate with WordPress REST API or direct DB access
329
+ const posts = await fetchPostsWithBlocks(blockName);
330
+
331
+ for (const post of posts) {
332
+ const blocks = extractBlocksFromPost(post, blockName);
333
+
334
+ for (const block of blocks) {
335
+ const analysis = detectBlockMigration(block.attributes, blockName);
336
+
337
+ if (analysis.needsMigration || analysis.warnings.length > 0) {
338
+ results.push({
339
+ blockName,
340
+ postId: post.id,
341
+ postTitle: post.title,
342
+ analysis,
343
+ attributes: block.attributes
344
+ });
345
+ }
346
+ }
347
+ }
348
+
349
+ console.log(`✅ Scan complete: ${results.length} blocks need attention`);
350
+
351
+ } catch (error) {
352
+ console.error('❌ Site scan failed:', error);
353
+ }
354
+
355
+ return results;
356
+ }
357
+
358
+ /**
359
+ * Generate migration report for WordPress admin
360
+ */
361
+ export function generateMigrationReport(scanResults: BlockScanResult[]): string {
362
+ const needsMigration = scanResults.filter(r => r.analysis.needsMigration);
363
+ const hasWarnings = scanResults.filter(r => r.analysis.warnings.length > 0);
364
+
365
+ let report = `# {{title}} Migration Report\n\n`;
366
+ report += `📊 **Summary:**\n`;
367
+ report += `- Total blocks scanned: ${scanResults.length}\n`;
368
+ report += `- Blocks needing migration: ${needsMigration.length}\n`;
369
+ report += `- Blocks with warnings: ${hasWarnings.length}\n\n`;
370
+
371
+ if (needsMigration.length > 0) {
372
+ report += `## 🔧 Blocks Requiring Migration\n\n`;
373
+
374
+ const byVersion = needsMigration.reduce((acc, result) => {
375
+ const version = result.analysis.currentVersion;
376
+ if (!acc[version]) acc[version] = [];
377
+ acc[version].push(result);
378
+ return acc;
379
+ }, {} as Record<string, BlockScanResult[]>);
380
+
381
+ for (const [version, blocks] of Object.entries(byVersion)) {
382
+ report += `### Version ${version} (${blocks.length} blocks)\n`;
383
+ for (const block of blocks.slice(0, 5)) { // Show first 5
384
+ report += `- Post: "${block.postTitle}" (ID: ${block.postId})\n`;
385
+ report += ` - Confidence: ${(block.analysis.confidence * 100).toFixed(0)}%\n`;
386
+ report += ` - Changes: ${block.analysis.affectedFields.added.join(', ')}\n`;
387
+ }
388
+ if (blocks.length > 5) {
389
+ report += ` - ... and ${blocks.length - 5} more\n`;
390
+ }
391
+ report += `\n`;
392
+ }
393
+ }
394
+
395
+ if (hasWarnings.length > 0) {
396
+ report += `## ⚠️ Warnings\n\n`;
397
+ for (const result of hasWarnings.slice(0, 10)) {
398
+ report += `- "${result.postTitle}": ${result.analysis.warnings.join(', ')}\n`;
399
+ }
400
+ }
401
+
402
+ report += `\n---\n`;
403
+ report += `Generated: ${new Date().toLocaleString()}\n`;
404
+
405
+ return report;
406
+ }
407
+
408
+ // Mock functions - would be implemented with WordPress integration
409
+ async function fetchPostsWithBlocks(blockName: string): Promise<any[]> {
410
+ // Implementation would use WordPress REST API or wp.data
411
+ return [];
412
+ }
413
+
414
+ function extractBlocksFromPost(post: any, blockName: string): any[] {
415
+ // Implementation would parse post_content for blocks
416
+ return [];
417
+ }