blok0 0.1.0 โ†’ 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,244 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as crypto from 'crypto';
4
+
5
+ export interface BlockSource {
6
+ url: string;
7
+ id: number;
8
+ }
9
+
10
+ export interface BlockEntry {
11
+ id: number;
12
+ name: string;
13
+ slug: string;
14
+ dir: string;
15
+ configPath: string;
16
+ componentPath: string;
17
+ source: BlockSource & {
18
+ fetchedAt: string;
19
+ };
20
+ checksums: {
21
+ [filename: string]: string;
22
+ };
23
+ }
24
+
25
+ export interface RegistryData {
26
+ version: string;
27
+ blocks: {
28
+ [slug: string]: BlockEntry;
29
+ };
30
+ }
31
+
32
+ const REGISTRY_FILE = 'blok0-registry.json';
33
+ const REGISTRY_VERSION = '1.0';
34
+
35
+ /**
36
+ * Get registry file path
37
+ */
38
+ function getRegistryPath(): string {
39
+ return path.join(process.cwd(), REGISTRY_FILE);
40
+ }
41
+
42
+ /**
43
+ * Load registry from file
44
+ */
45
+ export function loadRegistry(): RegistryData {
46
+ const registryPath = getRegistryPath();
47
+
48
+ if (!fs.existsSync(registryPath)) {
49
+ return {
50
+ version: REGISTRY_VERSION,
51
+ blocks: {}
52
+ };
53
+ }
54
+
55
+ try {
56
+ const data = fs.readFileSync(registryPath, 'utf-8');
57
+ const registry = JSON.parse(data);
58
+
59
+ // Validate registry structure
60
+ if (!registry.version || !registry.blocks) {
61
+ throw new Error('Invalid registry structure');
62
+ }
63
+
64
+ return registry;
65
+ } catch (error) {
66
+ throw new Error(`Failed to load registry: ${(error as Error).message}`);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Save registry to file
72
+ */
73
+ export function saveRegistry(registry: RegistryData): void {
74
+ const registryPath = getRegistryPath();
75
+
76
+ try {
77
+ fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2));
78
+ } catch (error) {
79
+ throw new Error(`Failed to save registry: ${(error as Error).message}`);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Check if block slug already exists in registry
85
+ */
86
+ export function isBlockRegistered(slug: string): boolean {
87
+ const registry = loadRegistry();
88
+ return slug in registry.blocks;
89
+ }
90
+
91
+ /**
92
+ * Get block entry by slug
93
+ */
94
+ export function getBlockEntry(slug: string): BlockEntry | null {
95
+ const registry = loadRegistry();
96
+ return registry.blocks[slug] || null;
97
+ }
98
+
99
+ /**
100
+ * Add block to registry
101
+ */
102
+ export function addBlockToRegistry(entry: BlockEntry): void {
103
+ const registry = loadRegistry();
104
+
105
+ if (entry.slug in registry.blocks) {
106
+ throw new Error(`Block with slug '${entry.slug}' is already registered`);
107
+ }
108
+
109
+ registry.blocks[entry.slug] = entry;
110
+ saveRegistry(registry);
111
+ }
112
+
113
+ /**
114
+ * Remove block from registry
115
+ */
116
+ export function removeBlockFromRegistry(slug: string): void {
117
+ const registry = loadRegistry();
118
+
119
+ if (!(slug in registry.blocks)) {
120
+ throw new Error(`Block with slug '${slug}' is not registered`);
121
+ }
122
+
123
+ delete registry.blocks[slug];
124
+ saveRegistry(registry);
125
+ }
126
+
127
+ /**
128
+ * Update block checksums
129
+ */
130
+ export function updateBlockChecksums(slug: string, checksums: { [filename: string]: string }): void {
131
+ const registry = loadRegistry();
132
+
133
+ if (!(slug in registry.blocks)) {
134
+ throw new Error(`Block with slug '${slug}' is not registered`);
135
+ }
136
+
137
+ registry.blocks[slug].checksums = checksums;
138
+ saveRegistry(registry);
139
+ }
140
+
141
+ /**
142
+ * Calculate file checksum
143
+ */
144
+ export function calculateChecksum(filePath: string): string {
145
+ const fileBuffer = fs.readFileSync(filePath);
146
+ const hashSum = crypto.createHash('sha256');
147
+ hashSum.update(fileBuffer);
148
+ return hashSum.digest('hex');
149
+ }
150
+
151
+ /**
152
+ * Calculate checksums for all files in a directory
153
+ */
154
+ export function calculateDirectoryChecksums(dirPath: string): { [filename: string]: string } {
155
+ const checksums: { [filename: string]: string } = {};
156
+
157
+ function walkDirectory(dir: string): void {
158
+ const files = fs.readdirSync(dir);
159
+
160
+ for (const file of files) {
161
+ const filePath = path.join(dir, file);
162
+ const stat = fs.statSync(filePath);
163
+
164
+ if (stat.isDirectory()) {
165
+ walkDirectory(filePath);
166
+ } else {
167
+ const relativePath = path.relative(dirPath, filePath);
168
+ checksums[relativePath] = calculateChecksum(filePath);
169
+ }
170
+ }
171
+ }
172
+
173
+ walkDirectory(dirPath);
174
+ return checksums;
175
+ }
176
+
177
+ /**
178
+ * Validate registry integrity
179
+ */
180
+ export function validateRegistry(): { valid: boolean; errors: string[] } {
181
+ const errors: string[] = [];
182
+
183
+ try {
184
+ const registry = loadRegistry();
185
+
186
+ for (const [slug, entry] of Object.entries(registry.blocks)) {
187
+ // Check if block directory exists
188
+ if (!fs.existsSync(entry.dir)) {
189
+ errors.push(`Block '${slug}': directory '${entry.dir}' does not exist`);
190
+ continue;
191
+ }
192
+
193
+ // Check if config file exists
194
+ if (!fs.existsSync(entry.configPath)) {
195
+ errors.push(`Block '${slug}': config file '${entry.configPath}' does not exist`);
196
+ }
197
+
198
+ // Check if component file exists
199
+ if (!fs.existsSync(entry.componentPath)) {
200
+ errors.push(`Block '${slug}': component file '${entry.componentPath}' does not exist`);
201
+ }
202
+
203
+ // Validate checksums if they exist
204
+ if (entry.checksums) {
205
+ for (const [file, expectedChecksum] of Object.entries(entry.checksums)) {
206
+ const filePath = path.join(entry.dir, file);
207
+ if (fs.existsSync(filePath)) {
208
+ const actualChecksum = calculateChecksum(filePath);
209
+ if (actualChecksum !== expectedChecksum) {
210
+ errors.push(`Block '${slug}': checksum mismatch for '${file}'`);
211
+ }
212
+ } else {
213
+ errors.push(`Block '${slug}': file '${file}' referenced in checksums does not exist`);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ } catch (error) {
219
+ errors.push(`Registry validation failed: ${(error as Error).message}`);
220
+ }
221
+
222
+ return {
223
+ valid: errors.length === 0,
224
+ errors
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Create empty registry for new projects
230
+ */
231
+ export function createEmptyRegistry(): void {
232
+ const registryPath = getRegistryPath();
233
+
234
+ if (fs.existsSync(registryPath)) {
235
+ throw new Error('Registry already exists');
236
+ }
237
+
238
+ const emptyRegistry: RegistryData = {
239
+ version: REGISTRY_VERSION,
240
+ blocks: {}
241
+ };
242
+
243
+ saveRegistry(emptyRegistry);
244
+ }
package/test-ast.js ADDED
@@ -0,0 +1,150 @@
1
+ const { updatePageCollectionConfig, updateRenderBlocksComponent, findPagesCollection, extractComponentName } = require('./dist/ast/index.js');
2
+ const { slugToIdentifier } = require('./dist/blocks/index.js');
3
+ const fs = require('fs');
4
+
5
+ // Test case: Add editorial--carousel block
6
+ // Simulates: blok0 add-block https://www.blok0.xyz/api/cli/sections/editorial--carousel
7
+
8
+ console.log('๐Ÿงช Testing Editorial Carousel Block Addition');
9
+ console.log('==========================================');
10
+ console.log('');
11
+
12
+ // Change to Payload project directory
13
+ const payloadProjectPath = '../newcms';
14
+ console.log(`๐Ÿ“ Changing to Payload project directory: ${payloadProjectPath}`);
15
+ try {
16
+ process.chdir(payloadProjectPath);
17
+ console.log('โœ… Successfully changed directory');
18
+ } catch (error) {
19
+ console.log('โŒ Failed to change directory:', error.message);
20
+ process.exit(1);
21
+ }
22
+
23
+ console.log('');
24
+ console.log('๐Ÿ” Finding Pages collection...');
25
+ const pagesPath = findPagesCollection();
26
+ console.log('Pages collection path:', pagesPath);
27
+
28
+ if (pagesPath) {
29
+ console.log('');
30
+ console.log('๐Ÿงช Testing AST update for editorial--carousel block...');
31
+
32
+ const blockSlug = 'editorial--carousel';
33
+ const blockIdentifier = slugToIdentifier(blockSlug);
34
+ const blockConfigPath = `@/blocks/${blockSlug}/config`;
35
+
36
+ console.log(`Block slug: ${blockSlug}`);
37
+ console.log(`Block identifier: ${blockIdentifier}`);
38
+ console.log(`Config path: ${blockConfigPath}`);
39
+ console.log('');
40
+
41
+ // Read file before modification
42
+ const originalContent = fs.readFileSync(pagesPath, 'utf-8');
43
+ const hasOriginalImport = originalContent.includes(`import { ${blockIdentifier} } from '${blockConfigPath}'`);
44
+ const hasOriginalBlock = originalContent.includes(blockIdentifier);
45
+
46
+ console.log('๐Ÿ“‹ Before modification:');
47
+ console.log(` - Import present: ${hasOriginalImport}`);
48
+ console.log(` - Block in array: ${hasOriginalBlock}`);
49
+ console.log('');
50
+
51
+ try {
52
+ updatePageCollectionConfig(pagesPath, blockConfigPath, blockIdentifier);
53
+
54
+ // Read file after modification
55
+ const updatedContent = fs.readFileSync(pagesPath, 'utf-8');
56
+ const hasUpdatedImport = updatedContent.includes(`import { ${blockIdentifier} } from '${blockConfigPath}'`);
57
+ const hasUpdatedBlock = updatedContent.includes(blockIdentifier);
58
+
59
+ console.log('๐Ÿ“‹ After Pages modification:');
60
+ console.log(` - Import present: ${hasUpdatedImport}`);
61
+ console.log(` - Block in array: ${hasUpdatedBlock}`);
62
+ console.log('');
63
+
64
+ if (hasUpdatedImport && hasUpdatedBlock) {
65
+ console.log('โœ… Pages collection update successful!');
66
+ } else {
67
+ console.log('โš ๏ธ Pages collection update issues:');
68
+ if (!hasUpdatedImport) console.log(' - โŒ Import was not added');
69
+ if (!hasUpdatedBlock) console.log(' - โŒ Block was not added to array');
70
+ process.exit(1);
71
+ }
72
+
73
+ // Now test RenderBlocks update
74
+ console.log('๐Ÿงช Testing RenderBlocks component update...');
75
+
76
+ const renderBlocksPath = '../newcms/src/blocks/RenderBlocks.tsx';
77
+ const componentPath = `./${blockSlug}/Component`;
78
+
79
+ // Check if the component file exists (it won't for editorial-carousel in test)
80
+ const fullComponentPath = require('path').resolve(componentPath.replace('./', 'src/blocks/') + '.tsx');
81
+ const componentExists = fs.existsSync(fullComponentPath);
82
+
83
+ if (componentExists) {
84
+ console.log('๐Ÿ“„ Component file exists, testing full RenderBlocks update...');
85
+
86
+ // Extract the actual component name
87
+ const actualComponentName = extractComponentName(fullComponentPath);
88
+ const blockTypeKey = 'editorialCarousel';
89
+
90
+ console.log(`Extracted component name: ${actualComponentName}`);
91
+ console.log(`Block type key: ${blockTypeKey}`);
92
+
93
+ // Read RenderBlocks before modification
94
+ const originalRenderBlocksContent = fs.readFileSync(renderBlocksPath, 'utf-8');
95
+ const hasOriginalComponentImport = originalRenderBlocksContent.includes(`import { ${actualComponentName} } from '${componentPath}'`);
96
+ const hasOriginalBlockComponent = originalRenderBlocksContent.includes(`'${blockTypeKey}': ${actualComponentName}`);
97
+
98
+ console.log('๐Ÿ“‹ RenderBlocks before modification:');
99
+ console.log(` - Component import present: ${hasOriginalComponentImport}`);
100
+ console.log(` - Block component mapping present: ${hasOriginalBlockComponent}`);
101
+ console.log('');
102
+
103
+ updateRenderBlocksComponent(renderBlocksPath, blockSlug, componentPath);
104
+
105
+ // Read RenderBlocks after modification
106
+ const updatedRenderBlocksContent = fs.readFileSync(renderBlocksPath, 'utf-8');
107
+ const hasUpdatedComponentImport = updatedRenderBlocksContent.includes(`import { ${actualComponentName} } from '${componentPath}'`);
108
+ const hasUpdatedBlockComponent = updatedRenderBlocksContent.includes(`'${blockTypeKey}': ${actualComponentName}`);
109
+
110
+ console.log('๐Ÿ“‹ RenderBlocks after modification:');
111
+ console.log(` - Component import present: ${hasUpdatedComponentImport}`);
112
+ console.log(` - Block component mapping present: ${hasUpdatedBlockComponent}`);
113
+ console.log('');
114
+
115
+ if (hasUpdatedComponentImport && hasUpdatedBlockComponent) {
116
+ console.log('โœ… RenderBlocks update successful! Component and mapping added.');
117
+ console.log('');
118
+ console.log('๐ŸŽ‰ Complete test passed! Pages collection and RenderBlocks updates work correctly.');
119
+ } else {
120
+ console.log('โš ๏ธ RenderBlocks update issues:');
121
+ if (!hasUpdatedComponentImport) console.log(' - โŒ Component import was not added');
122
+ if (!hasUpdatedBlockComponent) console.log(' - โŒ Block component mapping was not added');
123
+ console.log('');
124
+ console.log('๐Ÿ’ฅ Test failed! RenderBlocks implementation has issues.');
125
+ process.exit(1);
126
+ }
127
+ } else {
128
+ console.log('๐Ÿ“„ Component file does not exist (expected for test), testing error handling...');
129
+
130
+ try {
131
+ updateRenderBlocksComponent(renderBlocksPath, blockSlug, componentPath);
132
+ console.log('โŒ Expected error but function succeeded unexpectedly');
133
+ process.exit(1);
134
+ } catch (error) {
135
+ console.log('โœ… Function correctly failed with error:', error.message);
136
+ console.log('');
137
+ console.log('๐ŸŽ‰ Test passed! Function properly handles missing component files.');
138
+ }
139
+ }
140
+ } catch (error) {
141
+ console.log('โŒ AST update failed:', error.message);
142
+ console.log('');
143
+ console.log('๐Ÿ’ฅ Test failed! The bug may not be fully fixed.');
144
+ process.exit(1);
145
+ }
146
+ } else {
147
+ console.log('โŒ Could not find Pages collection in Payload project');
148
+ console.log('Make sure you are running this from a valid Payload CMS project directory.');
149
+ process.exit(1);
150
+ }
package/tsconfig.json CHANGED
@@ -1,16 +1,16 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "commonjs",
5
- "outDir": "./dist",
6
- "rootDir": "./src",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "declaration": true,
12
- "resolveJsonModule": true
13
- },
14
- "include": ["src/**/*.ts"],
15
- "exclude": ["node_modules", "dist", "src/templates/**"]
16
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "declaration": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src/**/*.ts"],
15
+ "exclude": ["node_modules", "dist", "src/templates/**"]
16
+ }