cloving 0.3.3 → 0.3.4

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.
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.applyAndSaveCurrentNewBlocks = exports.extractCurrentNewBlocks = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
6
+ exports.checkBlocksApplicability = exports.applyAndSaveCurrentNewBlocks = exports.processBlock = exports.updateFileContent = exports.writeFileContent = exports.ensureDirectoryExists = exports.extractCurrentNewBlocks = exports.extractBlock = exports.findBlockIndices = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const colors_1 = __importDefault(require("colors"));
@@ -35,13 +35,13 @@ exports.estimateTokens = estimateTokens;
35
35
  * or a partial JSON string.
36
36
  *
37
37
  * @example
38
- * const aiResponse = '```json\n{"key": "value"}\n```\nOther text';
39
- * const jsonMetadata = extractJsonMetadata(aiResponse);
38
+ * const aiResponse = '```json\n{"key": "value"}\n```\nOther text'
39
+ * const jsonMetadata = extractJsonMetadata(aiResponse)
40
40
  * // jsonMetadata will be '{"key": "value"}'
41
41
  *
42
42
  * @example
43
- * const plainResponse = 'Some text {"key": "value"} more text';
44
- * const jsonMetadata = extractJsonMetadata(plainResponse);
43
+ * const plainResponse = 'Some text {"key": "value"} more text'
44
+ * const jsonMetadata = extractJsonMetadata(plainResponse)
45
45
  * // jsonMetadata will be '{"key": "value"}'
46
46
  */
47
47
  const extractJsonMetadata = (response) => {
@@ -145,12 +145,14 @@ const findBlockIndices = (input, startIndex) => {
145
145
  end: blockEnd
146
146
  };
147
147
  };
148
+ exports.findBlockIndices = findBlockIndices;
148
149
  const extractBlock = (input, indices) => {
149
150
  const filePath = input.slice(indices.start + BLOCK_START.length, indices.filePathEnd).trim();
150
151
  const currentContent = input.slice(indices.filePathEnd + 1, indices.divider).trim();
151
152
  const newContent = input.slice(indices.divider + BLOCK_DIVIDER.length, indices.end).trim();
152
153
  return { filePath, currentContent, newContent };
153
154
  };
155
+ exports.extractBlock = extractBlock;
154
156
  /**
155
157
  * Extracts CURRENT/NEW blocks from the given input string.
156
158
  *
@@ -169,12 +171,12 @@ const extractBlock = (input, indices) => {
169
171
  * @example
170
172
  * const input = `
171
173
  * <<<<<<< CURRENT path/to/file.ts
172
- * const oldCode = 'old';
174
+ * const oldCode = 'old'
173
175
  * =======
174
- * const newCode = 'new';
176
+ * const newCode = 'new'
175
177
  * >>>>>>> NEW
176
- * `;
177
- * const blocks = extractCurrentNewBlocks(input);
178
+ * `
179
+ * const blocks = extractCurrentNewBlocks(input)
178
180
  * // blocks will contain one CurrentNewBlock object with the parsed information
179
181
  */
180
182
  const extractCurrentNewBlocks = (input) => {
@@ -183,10 +185,10 @@ const extractCurrentNewBlocks = (input) => {
183
185
  const blocks = [];
184
186
  let currentIndex = 0;
185
187
  while (currentIndex < input.length) {
186
- const indices = findBlockIndices(input, currentIndex);
188
+ const indices = (0, exports.findBlockIndices)(input, currentIndex);
187
189
  if (!indices)
188
190
  break;
189
- blocks.push(extractBlock(input, indices));
191
+ blocks.push((0, exports.extractBlock)(input, indices));
190
192
  currentIndex = indices.end + BLOCK_END.length;
191
193
  }
192
194
  return blocks;
@@ -206,10 +208,12 @@ const readFileContent = async (filePath) => {
206
208
  const ensureDirectoryExists = async (filePath) => {
207
209
  await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
208
210
  };
211
+ exports.ensureDirectoryExists = ensureDirectoryExists;
209
212
  const writeFileContent = async (filePath, content) => {
210
- await ensureDirectoryExists(filePath);
213
+ await (0, exports.ensureDirectoryExists)(filePath);
211
214
  await fs_1.default.promises.writeFile(filePath, content);
212
215
  };
216
+ exports.writeFileContent = writeFileContent;
213
217
  /**
214
218
  * Updates the content of a file by replacing a specified block of current content with new content.
215
219
  *
@@ -224,61 +228,55 @@ const writeFileContent = async (filePath, content) => {
224
228
  * @example
225
229
  * const currentContent = `
226
230
  * function example() {
227
- * console.log('Old content');
231
+ * console.log('Old content')
228
232
  * }
229
- * `;
233
+ * `
230
234
  * const block = {
231
- * currentContent: "console.log('Old content');",
232
- * newContent: "console.log('New content');"
233
- * };
234
- * const updatedContent = updateFileContent(currentContent, block);
235
+ * currentContent: "console.log('Old content')",
236
+ * newContent: "console.log('New content')"
237
+ * }
238
+ * const updatedContent = updateFileContent(currentContent, block)
235
239
  * // updatedContent will be:
236
240
  * // function example() {
237
- * // console.log('New content');
241
+ * // console.log('New content')
238
242
  * // }
239
243
  */
240
244
  const updateFileContent = (currentContent, block) => {
245
+ // 1) if block.currentContent.trim() is empty then return block.newContent
241
246
  if (block.currentContent.trim() === '') {
242
247
  return block.newContent;
243
248
  }
244
- // Normalize the current content for comparison
245
- const normalizedCurrentContent = block.currentContent.trim();
246
- const normalizedFileContent = currentContent.replace(/\r\n/g, '\n');
247
- // Find the index of the normalized current content in the file content
248
- const index = normalizedFileContent.indexOf(normalizedCurrentContent);
249
- if (index === -1) {
250
- return currentContent;
249
+ // 2) split currentContent and block.currentContent into new lines and trim() all the lines, then put back the new lines
250
+ const currentLines = currentContent.split('\n').map(line => line.trim());
251
+ const blockLines = block.currentContent.split('\n').map(line => line.trim());
252
+ // 3) see if the trimmed block.currentContent matches a section of code in the trimmed currentContent
253
+ const blockContent = blockLines.join('\n');
254
+ const currentContentTrimmed = currentLines.join('\n');
255
+ // 4) if so, make a variable named matchingLine with the line number of which line the block.currentContent starts on in currentContent
256
+ const matchingLine = currentContentTrimmed.indexOf(blockContent);
257
+ if (matchingLine === -1) {
258
+ return currentContent; // If no match is found, return the original content
259
+ }
260
+ // 5) compare the first line of block.currentContent with the matchingLine line of currentContent, putting any preceeding spaces that are in currentContent but not in block.currentContent into a variable named preceedingSpaces
261
+ const firstLineIndex = currentContent.indexOf(blockLines[0]);
262
+ const precedingSpaces = currentContent.substring(firstLineIndex, firstLineIndex + currentLines[0].length).match(/^\s*/);
263
+ // 6) add preceedingSpaces to the start of each line in both block.currentContent and block.newContent
264
+ if (precedingSpaces) {
265
+ const precedingSpacesValue = precedingSpaces[0];
266
+ const indentedNewContent = block.newContent.split('\n').map(line => precedingSpacesValue + line).join('\n');
267
+ // 7) finally, do a simple currentContent.replace(block.currentContent, block.newContent) with the modified (added preceeding spaces to match) variables
268
+ return currentContent.replace(block.currentContent, indentedNewContent);
269
+ }
270
+ else {
271
+ return currentContent.replace(block.currentContent, block.newContent);
251
272
  }
252
- // Determine the indentation level of the current content in the file
253
- const linesBefore = normalizedFileContent.substring(0, index).split('\n');
254
- const lastLineBefore = linesBefore[linesBefore.length - 1];
255
- const indentationMatch = lastLineBefore.match(/^\s*/);
256
- const indentation = indentationMatch ? indentationMatch[0] : '';
257
- // Calculate missing leading spaces in the current content
258
- const currentContentLines = block.currentContent.split('\n');
259
- const fileContentLines = normalizedFileContent.substring(index, index + normalizedCurrentContent.length).split('\n');
260
- const missingSpaces = currentContentLines.map((line, i) => {
261
- const fileLine = fileContentLines[i] || '';
262
- const lineIndentationMatch = fileLine.match(/^\s*/);
263
- const lineIndentation = lineIndentationMatch ? lineIndentationMatch[0] : '';
264
- return Math.max(0, lineIndentation.length - line.length + line.trimStart().length);
265
- });
266
- // Apply the adjusted indentation to the new content
267
- const indentedNewContent = block.newContent
268
- .split('\n')
269
- .map((line, i) => {
270
- const additionalSpaces = ' '.repeat(missingSpaces[i] || 0);
271
- return line.trim() ? indentation + additionalSpaces + line : line;
272
- })
273
- .join('\n');
274
- // Replace the current content with the indented new content
275
- return currentContent.replace(block.currentContent, indentedNewContent);
276
273
  };
274
+ exports.updateFileContent = updateFileContent;
277
275
  const processBlock = async (block, index) => {
278
276
  const filePath = path_1.default.resolve(block.filePath);
279
277
  let fileContent = await readFileContent(filePath);
280
278
  if (fileContent === '') {
281
- await writeFileContent(filePath, block.newContent);
279
+ await (0, exports.writeFileContent)(filePath, block.newContent);
282
280
  console.log(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} has been ${colors_1.default.green.bold('created')}`);
283
281
  return;
284
282
  }
@@ -286,10 +284,11 @@ const processBlock = async (block, index) => {
286
284
  console.log(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} ${colors_1.default.red.bold(`ERROR: Current content not found to replace in the file`)}`);
287
285
  return;
288
286
  }
289
- const updatedContent = updateFileContent(fileContent, block);
290
- await writeFileContent(filePath, updatedContent);
287
+ const updatedContent = (0, exports.updateFileContent)(fileContent, block);
288
+ await (0, exports.writeFileContent)(filePath, updatedContent);
291
289
  console.log(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} has been ${colors_1.default.yellow.bold('updated')}`);
292
290
  };
291
+ exports.processBlock = processBlock;
293
292
  /**
294
293
  * Applies and saves the changes specified in the CurrentNewBlock array.
295
294
  *
@@ -310,14 +309,14 @@ const processBlock = async (block, index) => {
310
309
  * const blocks = [
311
310
  * { filePath: 'path/to/file.ts', currentContent: 'old code', newContent: 'new code' },
312
311
  * { filePath: 'path/to/newfile.ts', currentContent: '', newContent: 'new file content' }
313
- * ];
314
- * await applyAndSaveCurrentNewBlocks(blocks);
312
+ * ]
313
+ * await applyAndSaveCurrentNewBlocks(blocks)
315
314
  */
316
315
  const applyAndSaveCurrentNewBlocks = async (blocks) => {
317
316
  console.log(`\nApplying and saving current/new blocks...\n`);
318
317
  for (const [index, block] of blocks.entries()) {
319
318
  try {
320
- await processBlock(block, index);
319
+ await (0, exports.processBlock)(block, index);
321
320
  }
322
321
  catch (error) {
323
322
  console.error(`Error processing ${colors_1.default.red(block.filePath)}:`, error);
@@ -325,3 +324,42 @@ const applyAndSaveCurrentNewBlocks = async (blocks) => {
325
324
  }
326
325
  };
327
326
  exports.applyAndSaveCurrentNewBlocks = applyAndSaveCurrentNewBlocks;
327
+ /**
328
+ * Checks if all the blocks can be applied without making any changes.
329
+ *
330
+ * This function iterates through an array of CurrentNewBlock objects and checks
331
+ * if each block can be applied to its respective file. It returns a tuple where
332
+ * the first element is a boolean indicating if all blocks can be applied, and
333
+ * the second element is a string summarizing the success or failure of each block.
334
+ *
335
+ * @param {CurrentNewBlock[]} blocks - An array of CurrentNewBlock objects to be checked.
336
+ * @returns {Promise<[boolean, string]>} A promise that resolves to a tuple containing
337
+ * a boolean and a summary string.
338
+ *
339
+ * @example
340
+ * const blocks = [
341
+ * { filePath: 'path/to/file.ts', currentContent: 'old code', newContent: 'new code' },
342
+ * { filePath: 'path/to/newfile.ts', currentContent: '', newContent: 'new file content' }
343
+ * ]
344
+ * const [canApply, summary] = await checkBlocksApplicability(blocks)
345
+ */
346
+ const checkBlocksApplicability = async (blocks) => {
347
+ let allApplicable = true;
348
+ const summary = [];
349
+ for (const [index, block] of blocks.entries()) {
350
+ const filePath = path_1.default.resolve(block.filePath);
351
+ let fileContent = await readFileContent(filePath);
352
+ if (fileContent === '') {
353
+ summary.push(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} can be ${colors_1.default.green.bold('created')}`);
354
+ }
355
+ else if (block.currentContent.trim() !== '' && !fileContent.includes(block.currentContent)) {
356
+ allApplicable = false;
357
+ summary.push(`[${index + 1}] ${colors_1.default.red.bold(block.filePath)} ${colors_1.default.red.bold('cannot be applied: Current content not found')}`);
358
+ }
359
+ else {
360
+ summary.push(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} can be ${colors_1.default.yellow.bold('updated')}`);
361
+ }
362
+ }
363
+ return [allApplicable, summary.join('\n')];
364
+ };
365
+ exports.checkBlocksApplicability = checkBlocksApplicability;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",