Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: docusaurus support #376

Merged
merged 129 commits into from
Jul 4, 2024
Merged

feat: docusaurus support #376

merged 129 commits into from
Jul 4, 2024

Conversation

songkg7
Copy link
Owner

@songkg7 songkg7 commented Jun 27, 2024

PR Checklist

Please check if your PR fulfills the following requirements:

  • Tests for the changes have been added (for bugfixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

  • Only supported Jekyll

Issue Number: #19 #346

What is the new behavior?

  • Docusaurus 3.x support
  • BREAKING CHANGE: No longer move files for backup. the file is converted with a copy in place and the copy is removed

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

  • node version update to LTS
  • update checkout action

Summary by CodeRabbit

  • New Features
    • Introduced a new CalloutConverter class for converting callout syntax.
    • Added CommentsConverter, CurlyBraceConverter, EmbedsConverter, and FootnotesConverter classes for specific syntax conversions.
    • New functions for filename conversion and front matter parsing introduced in FilenameConverter and FrontMatterConverter.
  • Documentation
    • Updated README.md with new syntax adjustments and formatting changes.
  • Chores
    • Updated GitHub Actions checkout version from v3 to v4 with additional permissions.
    • Node.js version specification changed to lts in configuration files.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range and nitpick comments (1)
src/jekyll/chirpy.ts (1)

Line range hint 56-99: Enhance error handling and logging in convertToChirpy.

While the function has some error handling, adding more descriptive logging can help in troubleshooting and maintaining the code.

  await validateSettings(plugin, settings);
  try {
    const markdownFiles = await copyMarkdownFile(plugin);
    for (const file of markdownFiles) {
      const fileName = convertFileName(file.name);
      const frontMatterConverter = new FrontMatterConverter(
        fileName,
        settings.jekyllRelativeResourcePath,
        settings.isEnableBanner,
        settings.isEnableUpdateFrontmatterTimeOnEdit,
      );
      const resourceLinkConverter = new ResourceLinkConverter(
        fileName,
        settings.resourcePath(),
        vaultAbsolutePath(plugin),
        settings.attachmentsFolder,
        settings.jekyllRelativeResourcePath,
      );
      const curlyBraceConverter = new CurlyBraceConverter(
        settings.isEnableCurlyBraceConvertMode,
      );
      const result = ConverterChain.create()
        .chaining(frontMatterConverter)
        .chaining(resourceLinkConverter)
        .chaining(curlyBraceConverter)
        .chaining(new WikiLinkConverter())
        .chaining(new CalloutConverter())
        .chaining(new FootnotesConverter())
        .chaining(new CommentsConverter())
        .chaining(new EmbedsConverter())
        .converting(await plugin.app.vault.read(file));

      await plugin.app.vault.modify(file, result);
      await moveFiles(
        file,
        `${vaultAbsolutePath(plugin)}/${settings.readyFolder}`,
        settings.targetPath(),
        settings.pathReplacer,
      )
        .then(() => new Notice('Moved files to Chirpy successfully.', 5000));
    }
  } catch (e) {
    console.error('Error during Chirpy conversion:', e);
    new Notice('Chirpy conversion failed, see console for more information.');
  }
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 6c41935 and 966b14e.

Files selected for processing (4)
  • src/docusaurus/docusaurus.ts (1 hunks)
  • src/jekyll/FrontMatterConverter.ts (4 hunks)
  • src/jekyll/chirpy.ts (2 hunks)
  • src/utils.ts (2 hunks)
Files not reviewed due to errors (1)
  • src/jekyll/FrontMatterConverter.ts (no review received)
Files skipped from review as they are similar to previous changes (2)
  • src/docusaurus/docusaurus.ts
  • src/utils.ts
Additional context used
Learnings (1)
src/jekyll/FrontMatterConverter.ts (1)
Learnt from: songkg7
PR: songkg7/o2#376
File: src/jekyll/FrontMatterConverter.ts:9-29
Timestamp: 2024-06-28T03:24:10.861Z
Learning: In `js-yaml` version 4, `yaml.safeLoad` has been removed, and `yaml.load` is safe by default and does not execute arbitrary code.
Biome
src/jekyll/FrontMatterConverter.ts

[error] 145-145: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 156-156: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 157-157: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 158-158: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

src/jekyll/chirpy.ts Outdated Show resolved Hide resolved
songkg7 and others added 2 commits June 29, 2024 21:50
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 966b14e and 2ab938a.

Files selected for processing (4)
  • README.md (2 hunks)
  • src/docusaurus/docusaurus.ts (1 hunks)
  • src/jekyll/chirpy.ts (2 hunks)
  • src/utils.ts (2 hunks)
Files skipped from review as they are similar to previous changes (3)
  • src/docusaurus/docusaurus.ts
  • src/jekyll/chirpy.ts
  • src/utils.ts
Additional context used
LanguageTool
README.md

[uncategorized] ~56-~56: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...the assets/img/<yyyy-MM-dd> folder. - Auto create folders: If enabled, the plugin will au...

(AUTO_HYPHEN)


[style] ~94-~94: Using many exclamation marks might seem excessive (in this case: 13 exclamation marks for a text that’s 4247 characters long)
Context: ...B0/) Welcome to write articles about O2! ## License This project is published ...

(EN_EXCESSIVE_EXCLAMATION)

Markdownlint
README.md

106-106: Expected: 1; Actual: 2
Multiple consecutive blank lines

(MD012, no-multiple-blanks)

Additional comments not posted (4)
README.md (4)

57-57: LGTM!


63-63: LGTM!


65-65: LGTM!


66-66: LGTM!

README.md Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Outside diff range and nitpick comments (1)
src/jekyll/FrontMatterConverter.ts (1)

Line range hint 134-149: Consider Returning a New Object and Avoid Using delete

Modifying the input object directly can lead to side effects. Consider returning a new object instead to ensure immutability. Also, avoid using the delete operator for performance reasons.

- delete frontMatter.updated;
+ const newFrontMatter = { ...frontMatter };
+ newFrontMatter.updated = undefined;
+ return newFrontMatter;
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 2ab938a and 5a246c1.

Files selected for processing (5)
  • src/docusaurus/docusaurus.ts (1 hunks)
  • src/jekyll/FrontMatterConverter.ts (4 hunks)
  • src/tests/FootnotesConverter.test.ts (2 hunks)
  • src/tests/FrontMatterConverter.test.ts (3 hunks)
  • src/utils.ts (2 hunks)
Files skipped from review as they are similar to previous changes (2)
  • src/docusaurus/docusaurus.ts
  • src/tests/FootnotesConverter.test.ts
Additional context used
Learnings (1)
src/jekyll/FrontMatterConverter.ts (1)
Learnt from: songkg7
PR: songkg7/o2#376
File: src/jekyll/FrontMatterConverter.ts:9-29
Timestamp: 2024-06-28T03:24:10.861Z
Learning: In `js-yaml` version 4, `yaml.safeLoad` has been removed, and `yaml.load` is safe by default and does not execute arbitrary code.
Biome
src/jekyll/FrontMatterConverter.ts

[error] 145-145: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 158-158: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 161-161: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 162-162: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Additional comments not posted (8)
src/jekyll/FrontMatterConverter.ts (2)

9-30: Correct Usage of yaml.load

The usage of yaml.load from the js-yaml library is appropriate here, considering the updates in version 4.


32-39: LGTM!

The join function correctly constructs a string from the front matter and body content.

src/utils.ts (5)

Line range hint 9-19: LGTM!

The vaultAbsolutePath function correctly retrieves the absolute path of the vault.


42-45: LGTM!

The getFilesInReady function correctly retrieves markdown files from the ready folder.


55-62: LGTM!

The copyFile function correctly copies a file from the source path to the target path.


127-144: LGTM!

The transformPath function correctly transforms a file path based on a date extraction pattern.


152-162: LGTM!

The parseLocalDate function correctly parses a date string into a LocalDate object.

src/tests/FrontMatterConverter.test.ts (1)

Line range hint 1-17: LGTM!

The test case correctly verifies that the convert method does not alter the input if the image conversion is disabled.

Comment on lines 112 to 131
function convertImageFrontMatter(
isEnable: boolean,
fileName: string,
resourcePath: string,
frontMatter: FrontMatter,
) {
if (!isEnable) {
return frontMatter;
}

if (ObsidianRegex.ATTACHMENT_LINK.test(frontMatter.image)) {
const match = frontMatter.image.match(ObsidianRegex.ATTACHMENT_LINK);
if (match) {
frontMatter.image = frontMatter.image.replace(ObsidianRegex.ATTACHMENT_LINK, '$1.$2');
}
}
frontMatter.image = convertImagePath(this.fileName, frontMatter.image, this.resourcePath);
if (!frontMatter.image) {
return frontMatter;
}

const match = ObsidianRegex.ATTACHMENT_LINK.exec(frontMatter.image);
if (match) {
frontMatter.image = `${match[1]}.${match[2]}`;
}
frontMatter.image = convertImagePath(fileName, frontMatter.image, resourcePath);
return frontMatter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Returning a New Object and Avoid Using delete

Modifying the input object directly can lead to side effects. Consider returning a new object instead to ensure immutability. Also, avoid using the delete operator for performance reasons.

- frontMatter.image = `${match[1]}.${match[2]}`;
- delete frontMatter['updated'];
+ const newFrontMatter = { ...frontMatter };
+ newFrontMatter.image = `${match[1]}.${match[2]}`;
+ newFrontMatter.updated = undefined;
+ return newFrontMatter;

Committable suggestion was skipped due to low confidence.

Comment on lines 150 to 168
export const convertFrontMatter = (input: string) => {
const [frontMatter, body] = parseFrontMatter(input);
if (Object.keys(frontMatter).length === 0) {
return input;
}

if (frontMatter.updated) {
frontMatter.date = frontMatter.updated;
delete frontMatter.updated;
}

delete frontMatter['aliases'];
delete frontMatter['published'];

return join(
convert({ ...frontMatter }),
body,
);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Returning a New Object and Avoid Using delete

Modifying the input object directly can lead to side effects. Consider returning a new object instead to ensure immutability. Also, avoid using the delete operator for performance reasons.

- delete frontMatter['aliases'];
- delete frontMatter['published'];
+ const newFrontMatter = { ...frontMatter };
+ newFrontMatter.aliases = undefined;
+ newFrontMatter.published = undefined;
+ return join(
+   convert(newFrontMatter),
+   body,
+ );
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const convertFrontMatter = (input: string) => {
const [frontMatter, body] = parseFrontMatter(input);
if (Object.keys(frontMatter).length === 0) {
return input;
}
if (frontMatter.updated) {
frontMatter.date = frontMatter.updated;
delete frontMatter.updated;
}
delete frontMatter['aliases'];
delete frontMatter['published'];
return join(
convert({ ...frontMatter }),
body,
);
};
export const convertFrontMatter = (input: string) => {
const [frontMatter, body] = parseFrontMatter(input);
if (Object.keys(frontMatter).length === 0) {
return input;
}
if (frontMatter.updated) {
frontMatter.date = frontMatter.updated;
frontMatter.updated = undefined;
}
const newFrontMatter = { ...frontMatter };
newFrontMatter.aliases = undefined;
newFrontMatter.published = undefined;
return join(
convert(newFrontMatter),
body,
);
};
Tools
Biome

[error] 158-158: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 161-161: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 162-162: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Comment on lines 40 to 59
const convert = (frontMatter: FrontMatter) => {
const fm = { ...frontMatter };
// if not around front matter title using double quote, add double quote
fm.title = fm.title?.startsWith('"') ? fm.title : `"${fm.title}"`;

// if not around front matter categories using an array, add an array
if (fm.categories && JSON.stringify(fm.categories).startsWith('[')) {
fm.categories = `${JSON.stringify(fm.categories)
.replace(/,/g, ', ')
.replace(/"/g, '')
}`;
}

// if fm.tags is array
if (fm.tags) {
fm.tags = Array.isArray(fm.tags) ? `[${fm.tags.join(', ')}]` : `[${fm.tags}]`;
}

return fm;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Returning a New Object and Avoid Using delete

Modifying the input object directly can lead to side effects. Consider returning a new object instead to ensure immutability. Also, avoid using the delete operator for performance reasons.

- return fm;
+ return { ...fm };
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const convert = (frontMatter: FrontMatter) => {
const fm = { ...frontMatter };
// if not around front matter title using double quote, add double quote
fm.title = fm.title?.startsWith('"') ? fm.title : `"${fm.title}"`;
// if not around front matter categories using an array, add an array
if (fm.categories && JSON.stringify(fm.categories).startsWith('[')) {
fm.categories = `${JSON.stringify(fm.categories)
.replace(/,/g, ', ')
.replace(/"/g, '')
}`;
}
// if fm.tags is array
if (fm.tags) {
fm.tags = Array.isArray(fm.tags) ? `[${fm.tags.join(', ')}]` : `[${fm.tags}]`;
}
return fm;
};
const convert = (frontMatter: FrontMatter) => {
const fm = { ...frontMatter };
// if not around front matter title using double quote, add double quote
fm.title = fm.title?.startsWith('"') ? fm.title : `"${fm.title}"`;
// if not around front matter categories using an array, add an array
if (fm.categories && JSON.stringify(fm.categories).startsWith('[')) {
fm.categories = `${JSON.stringify(fm.categories)
.replace(/,/g, ', ')
.replace(/"/g, '')
}`;
}
// if fm.tags is array
if (fm.tags) {
fm.tags = Array.isArray(fm.tags) ? `[${fm.tags.join(', ')}]` : `[${fm.tags}]`;
}
return { ...fm };
};

Comment on lines +98 to +111
export const moveFiles = async (
sourceFolderPath: string,
targetFolderPath: string,
pathReplacer: (year: string, month: string, day: string, title: string) => string,
publishedDate?: LocalDate,
) => {

copy(
sourceFolderPath,
targetFolderPath,
pathReplacer,
publishedDate,
);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Using Asynchronous File Operations in moveFiles

Using synchronous operations (fs.copyFileSync) in this context can block the Node.js event loop, especially with a large number of files. Consider refactoring to use asynchronous methods to improve performance and non-blocking behavior.

- fs.copyFileSync(sourceFilePath, targetFilePath);
+ fs.copyFile(sourceFilePath, targetFilePath, (err) => {
+   if (err) {
+     console.error('Error copying file:', err);
+     return;
+   }
+   console.log(`Copied ${sourceFilePath} to ${targetFilePath}`);
+ });

Committable suggestion was skipped due to low confidence.

Comment on lines +64 to +84
export const copy = (
sourceFolderPath: string,
targetFolderPath: string,
replacer: (year: string, month: string, day: string, title: string) => string,
publishedDate?: LocalDate,
) => {
fs.readdirSync(sourceFolderPath)
.filter(filename => filename.startsWith(TEMP_PREFIX))
.forEach((filename) => {
const transformedFileName = transformPath(
filename,
replacer,
publishedDate,
);

const sourceFilePath = path.join(sourceFolderPath, filename);
const targetFilePath = path.join(targetFolderPath, transformedFileName.replace(TEMP_PREFIX, '').replace(/\s/g, '-'));

copyFile(sourceFilePath, targetFilePath);
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Using Asynchronous File Operations in copy

Using synchronous operations (fs.copyFileSync) in this context can block the Node.js event loop, especially with a large number of files. Consider refactoring to use asynchronous methods to improve performance and non-blocking behavior.

- fs.copyFileSync(sourceFilePath, targetFilePath);
+ fs.copyFile(sourceFilePath, targetFilePath, (err) => {
+   if (err) {
+     console.error('Error copying file:', err);
+     return;
+   }
+   console.log(`Copied ${sourceFilePath} to ${targetFilePath}`);
+ });

Committable suggestion was skipped due to low confidence.

Comment on lines +86 to +96
export const achieve = async (plugin: O2Plugin, settings: O2PluginSettings) => {
if (!settings.isAutoAchieve) {
return;
}

// move files to achieve folder
const readyFiles = getFilesInReady(plugin);
readyFiles.forEach((file: TFile) => {
return plugin.app.vault.copy(file, file.path.replace(settings.readyFolder, settings.achieveFolder));
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure Proper Error Handling in achieve

The function currently does not handle errors that might occur during the file copy for achieving. Adding error handling can prevent silent failures and improve reliability.

+ try {
    return plugin.app.vault.copy(file, file.path.replace(settings.readyFolder, settings.achieveFolder));
+ } catch (error) {
+   console.error(`Failed to achieve file ${file.name}:`, error);
+   new Notice('Failed to achieve file, see console for more information.');
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const achieve = async (plugin: O2Plugin, settings: O2PluginSettings) => {
if (!settings.isAutoAchieve) {
return;
}
// move files to achieve folder
const readyFiles = getFilesInReady(plugin);
readyFiles.forEach((file: TFile) => {
return plugin.app.vault.copy(file, file.path.replace(settings.readyFolder, settings.achieveFolder));
});
};
export const achieve = async (plugin: O2Plugin, settings: O2PluginSettings) => {
if (!settings.isAutoAchieve) {
return;
}
// move files to achieve folder
const readyFiles = getFilesInReady(plugin);
readyFiles.forEach((file: TFile) => {
try {
return plugin.app.vault.copy(file, file.path.replace(settings.readyFolder, settings.achieveFolder));
} catch (error) {
console.error(`Failed to achieve file ${file.name}:`, error);
new Notice('Failed to achieve file, see console for more information.');
}
});
};

Comment on lines +46 to +53
export async function backupOriginalNotes(plugin: O2Plugin) {
const readyFiles = getFilesInReady(plugin);
const backupFolder = plugin.jekyll.achieveFolder;
const readyFolder = plugin.jekyll.readyFolder;
readyFiles.forEach((file: TFile) => {
return plugin.app.vault.copy(file, file.path.replace(readyFolder, backupFolder));
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure Proper Error Handling in backupOriginalNotes

The function currently does not handle errors that might occur during the file copy for backup. Adding error handling can prevent silent failures and improve reliability.

+ try {
    return plugin.app.vault.copy(file, file.path.replace(readyFolder, backupFolder));
+ } catch (error) {
+   console.error(`Failed to backup file ${file.name}:`, error);
+   new Notice('Failed to backup file, see console for more information.');
+ }

Committable suggestion was skipped due to low confidence.

Comment on lines +20 to +40
export const copyMarkdownFile = async (plugin: O2Plugin): Promise<TFile[]> => {
const dateString = Temporal.Now.plainDateISO().toString();
const markdownFiles = getFilesInReady(plugin);
for (const file of markdownFiles) {
const newFileName = TEMP_PREFIX + dateString + '-' + file.name;
const newPath = file.path
.replace(file.name, newFileName)
.replace(/,+/g, '')
.replace(/\s/g, '-');

await plugin.app.vault.copy(file, newPath)
.catch((error) => {
console.error(error);
new Notice('Failed to copy file, see console for more information.');
});
}

// collect copied files
return plugin.app.vault.getMarkdownFiles()
.filter((file: TFile) => file.path.includes(TEMP_PREFIX));
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Error Handling in copyMarkdownFile

The function attempts to handle errors during the file copy operation, but it returns a promise that might not be properly awaited by the callers. Ensure that the error handling is consistent and that callers are aware they need to handle potential rejections.

+ try {
    await plugin.app.vault.copy(file, newPath);
+ } catch (error) {
+   console.error(error);
+   new Notice('Failed to copy file, see console for more information.');
+ }

Committable suggestion was skipped due to low confidence.

Comment on lines +114 to +125
export const cleanUp = async (plugin: O2Plugin) => {
// remove temp files
const markdownFiles = plugin.app.vault.getMarkdownFiles()
.filter((file) => file.path.includes(TEMP_PREFIX));

for (const file of markdownFiles) {
await plugin.app.vault.delete(file)
.then(() => {
console.log(`Deleted temp file: ${file.path}`);
});
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Error Handling in cleanUp

The function attempts to handle errors during the file deletion operation, but it returns a promise that might not be properly awaited by the callers. Ensure that the error handling is consistent and that callers are aware they need to handle potential rejections.

+ try {
    await plugin.app.vault.delete(file);
+ } catch (error) {
+   console.error(`Failed to delete file ${file.name}:`, error);
+   new Notice('Failed to delete file, see console for more information.');
+ }

Committable suggestion was skipped due to low confidence.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Outside diff range and nitpick comments (1)
README.md (1)

Line range hint 94-94: Reduce excessive exclamation marks.

Using many exclamation marks might seem excessive. Consider reducing the number of exclamation marks for better style.

- If you ever want to buy me a coffee, don't hesitate.
+ If you ever want to buy me a coffee, don't hesitate!
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 5a246c1 and 3390695.

Files selected for processing (1)
  • README.md (2 hunks)
Additional context used
LanguageTool
README.md

[uncategorized] ~38-~38: Possible missing article found.
Context: ... via obsidian's cmd + p shortcut. if exception occurs, you can see the original note i...

(AI_HYDRA_LEO_MISSING_AN)


[style] ~94-~94: Using many exclamation marks might seem excessive (in this case: 13 exclamation marks for a text that’s 4247 characters long)
Context: ...B0/) Welcome to write articles about O2! ## License This project is published ...

(EN_EXCESSIVE_EXCLAMATION)

Markdownlint
README.md

106-106: Expected: 1; Actual: 2
Multiple consecutive blank lines

(MD012, no-multiple-blanks)

Additional comments not posted (3)
README.md (3)

56-57: Approved: Hyphen addition for "Auto-create folders".

The hyphen improves readability and consistency.


82-83: Approved: Sentence break fix.

The fix improves the clarity of the sentence.


63-68: Verify syntax examples for accuracy.

Ensure that the syntax examples for image resizing and embedding are correct.

Verification successful

Syntax examples are accurate.

The syntax examples for image resizing and embedding in the README.md file are correct and match the expected patterns.

  • Image resizing (width): ![image](/assets/img/<yyyy-MM-dd>-<title>/NewJeans.png){: width="100" }
  • Image resizing (width & height): ![image](/assets/img/<yyyy-MM-dd>-<title>/NewJeans.png){: width="100" height="200" }
  • Embeds: ![[Obsidian#This is Obsidian!]]
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify syntax examples for image resizing and embedding.

# Test: Search for syntax examples in the codebase. Expect: Correct syntax examples.
rg --type markdown 'image.*\{\: width="100" \}' && rg --type markdown 'image.*\{\: width="100" height="200" \}' && rg --type markdown 'Obsidian'

Length of output: 151



Script:

#!/bin/bash
# Description: Verify syntax examples for image resizing and embedding.

# Test: Search for syntax examples in the codebase. Expect: Correct syntax examples.

# Correct the regex patterns to avoid escape sequence errors
rg --type markdown 'image.*\{: width="100" \}' && rg --type markdown 'image.*\{: width="100" height="200" \}' && rg --type markdown 'Obsidian'

Length of output: 1864

@songkg7 songkg7 added the chirpy Related to Jekyll Chirpy theme label Jun 30, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Outside diff range and nitpick comments (4)
src/FilenameConverter.ts (1)

1-5: Ensure consistent handling of file extensions.

The convertFileName function currently only removes .md extensions. Consider making this more flexible to handle other extensions if needed.

// Example:
export const convertFileName = (filename: string): string =>
  filename
    .replace(/\.[^/.]+$/, '') // Removes any file extension
    .replace(/\s/g, '-')
    .replace(/[^a-zA-Z0-9-\uAC00-\uD7A3]/g, '');
src/CommentsConverter.ts (1)

1-2: Consider removing redundant import statements.

The Converter import is not used in this file. Consider removing it to clean up the code.

import { ObsidianRegex } from './core/ObsidianRegex';
src/WikiLinkConverter.ts (1)

1-2: Consider removing redundant import statements.

The Converter import is not used in this file. Consider removing it to clean up the code.

import { ObsidianRegex } from './core/ObsidianRegex';
src/ResourceLinkConverter.ts (1)

Line range hint 46-107: Improve error handling and use async/await for file operations.

The current implementation uses synchronous file operations and logs errors to the console. Consider using async/await for non-blocking operations and improve error handling.

import fs from 'fs/promises';

export class ResourceLinkConverter implements Converter {
  private readonly fileName: string;
  private readonly resourcePath: string;
  private readonly absolutePath: string;
  private readonly attachmentsFolder: string;
  private readonly relativeResourcePath: string;

  constructor(fileName: string, resourcePath: string, absolutePath: string, attachmentsFolder: string, relativeResourcePath: string) {
    this.fileName = fileName;
    this.resourcePath = resourcePath;
    this.absolutePath = absolutePath;
    this.attachmentsFolder = attachmentsFolder;
    this.relativeResourcePath = relativeResourcePath;
  }

  async convert(input: string): Promise<string> {
    const sanitizedFileName = removeTempPrefix(this.fileName);
    const resourcePath = `${this.resourcePath}/${sanitizedFileName}`;
    const resourceNames = extractResourceNames(input);
    if (resourceNames && resourceNames.length > 0) {
      await fs.mkdir(resourcePath, { recursive: true });
    }

    if (resourceNames) {
      for (const resourceName of resourceNames) {
        try {
          await fs.copyFile(
            `${this.absolutePath}/${this.attachmentsFolder}/${resourceName}`,
            `${resourcePath}/${resourceName.replace(/\s/g, '-')}`
          );
        } catch (err) {
          console.error(err);
          new Notice(err.message);
        }
      }
    }

    const replacer = (match: string, contents: string, suffix: string, width: string | undefined, height: string | undefined, space: string | undefined, caption: string | undefined) =>
      `![image](/${this.relativeResourcePath}/${sanitizedFileName}/${contents.replace(/\s/g, '-')}.${suffix})`
      + `${convertImageSize(width, height)}`
      + `${convertImageCaption(caption)}`;

    return input.replace(ObsidianRegex.ATTACHMENT_LINK, replacer);
  }
}
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 3390695 and edb0e65.

Files selected for processing (22)
  • src/CalloutConverter.ts (1 hunks)
  • src/CommentsConverter.ts (1 hunks)
  • src/CurlyBraceConverter.ts (2 hunks)
  • src/EmbedsConverter.ts (1 hunks)
  • src/FilenameConverter.ts (1 hunks)
  • src/FootnotesConverter.ts (1 hunks)
  • src/FrontMatterConverter.ts (1 hunks)
  • src/ResourceLinkConverter.ts (3 hunks)
  • src/WikiLinkConverter.ts (1 hunks)
  • src/core/ObsidianRegex.ts (1 hunks)
  • src/docusaurus/docusaurus.ts (1 hunks)
  • src/jekyll/chirpy.ts (2 hunks)
  • src/tests/CalloutConverter.test.ts (3 hunks)
  • src/tests/CommentsConverter.test.ts (1 hunks)
  • src/tests/CurlyBraceConverter.test.ts (1 hunks)
  • src/tests/EmbedsConverter.test.ts (1 hunks)
  • src/tests/FilenameConverter.test.ts (2 hunks)
  • src/tests/FootnotesConverter.test.ts (2 hunks)
  • src/tests/FrontMatterConverter.test.ts (3 hunks)
  • src/tests/ObsidianRegex.test.ts (1 hunks)
  • src/tests/ResourceLinkConverter.test.ts (1 hunks)
  • src/tests/WikiLinkConverter.test.ts (1 hunks)
Files skipped from review due to trivial changes (7)
  • src/EmbedsConverter.ts
  • src/tests/CommentsConverter.test.ts
  • src/tests/CurlyBraceConverter.test.ts
  • src/tests/EmbedsConverter.test.ts
  • src/tests/ObsidianRegex.test.ts
  • src/tests/ResourceLinkConverter.test.ts
  • src/tests/WikiLinkConverter.test.ts
Files skipped from review as they are similar to previous changes (5)
  • src/docusaurus/docusaurus.ts
  • src/tests/CalloutConverter.test.ts
  • src/tests/FilenameConverter.test.ts
  • src/tests/FootnotesConverter.test.ts
  • src/tests/FrontMatterConverter.test.ts
Additional context used
Biome
src/FrontMatterConverter.ts

[error] 145-145: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 158-158: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 161-161: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 162-162: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Additional comments not posted (18)
src/core/ObsidianRegex.ts (1)

5-5: Verify the correctness of the updated CALLOUT regex pattern.

The updated CALLOUT regex pattern includes an additional capturing group. Ensure this change is intended and correctly handled in the code that uses this regex.

Verification successful

The updated CALLOUT regex pattern is correctly handled by the jekyllReplacer and docusaurusReplacer functions.

  • The jekyllReplacer and docusaurusReplacer functions in src/CalloutConverter.ts are designed to handle three capturing groups, which aligns with the updated CALLOUT regex pattern.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the usage of the updated CALLOUT regex pattern.

# Test: Search for the usage of ObsidianRegex.CALLOUT. Expect: Correct handling of capturing groups.
rg --type typescript 'ObsidianRegex.CALLOUT'

Length of output: 79



Script:

#!/bin/bash
# Description: Verify the usage of the updated CALLOUT regex pattern.

# Test: Search for the usage of ObsidianRegex.CALLOUT. Expect: Correct handling of capturing groups.
rg --type ts 'ObsidianRegex.CALLOUT'

Length of output: 315



Script:

#!/bin/bash
# Description: Verify the handling of capturing groups in the updated CALLOUT regex pattern.

# Search for the definitions of jekyllReplacer and docusaurusReplacer in the codebase.
rg --type ts 'jekyllReplacer'
rg --type ts 'docusaurusReplacer'

Length of output: 596

src/CommentsConverter.ts (1)

4-9: Ensure consistency between class method and standalone function.

The convert method in CommentsConverter class and the standalone convertComments function perform the same operation. Ensure consistency in their implementations.

src/WikiLinkConverter.ts (1)

4-8: Ensure consistency between class method and standalone function.

The convert method in WikiLinkConverter class and the standalone convertWikiLink function perform the same operation. Ensure consistency in their implementations.

src/FootnotesConverter.ts (2)

1-11: LGTM!

The FootnotesConverter class correctly implements the Converter interface and uses a regex pattern to transform footnotes.


13-17: LGTM!

The convertFootnotes function correctly uses a regex pattern to transform footnotes.

src/CurlyBraceConverter.ts (2)

Line range hint 1-18: LGTM!

The CurlyBraceConverter class correctly implements the Converter interface and uses a regex pattern to transform content based on the isEnable flag.


20-25: LGTM!

The convertCurlyBrace function correctly uses a regex pattern to transform content based on the isEnable flag.

src/CalloutConverter.ts (5)

1-8: LGTM!

The CalloutConverter class correctly implements the Converter interface and uses a helper function to transform callout syntax.


10-12: LGTM!

The convertCalloutSyntaxToChirpy function correctly uses a regex pattern and a replacer function to transform callout syntax.


44-49: LGTM!

The convertDocusaurusCallout function correctly uses a regex pattern and a replacer function to transform callout syntax for Docusaurus.


51-52: LGTM!

The replaceDocusaurusKeyword function correctly maps callout keywords to Docusaurus-specific keywords.


81-82: LGTM!

The replaceDocusaurusContents function correctly transforms the contents of a callout.

src/jekyll/chirpy.ts (1)

Line range hint 55-96: LGTM!

The convertToChirpy function correctly performs multiple operations including file reads, transformations, and writes. Error handling and logging are included.

src/ResourceLinkConverter.ts (3)

2-7: Update import paths for consistency.

Ensure that all import paths are consistent and correct. The changes seem to align with the new directory structure.


Line range hint 108-115: LGTM!


Line range hint 116-137: LGTM!

src/FrontMatterConverter.ts (2)

1-4: Update import paths for consistency.

Ensure that all import paths are consistent and correct. The changes seem to align with the new directory structure.


32-39: LGTM!

Comment on lines +7 to +8
export const removeTempPrefix = (filename: string): string =>
filename.replace('o2-temp', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making the prefix to remove configurable.

The removeTempPrefix function currently removes a hardcoded prefix. Making this configurable can enhance flexibility.

export const removeTempPrefix = (filename: string, prefix: string = 'o2-temp'): string =>
  filename.replace(prefix, '');

Comment on lines +8 to +45
export const convertJekyllResourceLink = (
input: string,
fileName: string,
absolutePath: string,
settings: JekyllSettings,
) => {
const resourcePath = `${settings.resourcePath}/${fileName}`;
const resourceNames = extractResourceNames(input);
if (!(resourceNames === undefined || resourceNames.length === 0)) {
fs.mkdirSync(resourcePath, { recursive: true });
}
resourceNames?.forEach((resourceName) => {
fs.copyFile(
`${absolutePath}/${settings.attachmentsFolder}/${resourceName}`,
`${resourcePath}/${(resourceName.replace(/\s/g, '-'))}`,
(err) => {
if (err) {
// ignore error
console.error(err);
new Notice(err.message);
}
},
);
});

const replacer = (match: string,
contents: string,
suffix: string,
width: string | undefined,
height: string | undefined,
space: string | undefined,
caption: string | undefined) =>
`![image](/${settings.jekyllRelativeResourcePath}/${fileName}/${contents.replace(/\s/g, '-')}.${suffix})`
+ `${convertImageSize(width, height)}`
+ `${convertImageCaption(caption)}`;

return input.replace(ObsidianRegex.ATTACHMENT_LINK, replacer);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling and use async/await for file operations.

The current implementation uses synchronous file operations and logs errors to the console. Consider using async/await for non-blocking operations and improve error handling.

import fs from 'fs/promises';

export const convertJekyllResourceLink = async (
  input: string,
  fileName: string,
  absolutePath: string,
  settings: JekyllSettings,
) => {
  const resourcePath = `${settings.resourcePath}/${fileName}`;
  const resourceNames = extractResourceNames(input);
  if (resourceNames && resourceNames.length > 0) {
    await fs.mkdir(resourcePath, { recursive: true });
  }

  if (resourceNames) {
    for (const resourceName of resourceNames) {
      try {
        await fs.copyFile(
          `${absolutePath}/${settings.attachmentsFolder}/${resourceName}`,
          `${resourcePath}/${resourceName.replace(/\s/g, '-')}`
        );
      } catch (err) {
        console.error(err);
        new Notice(err.message);
      }
    }
  }

  const replacer = (match: string, contents: string, suffix: string, width: string | undefined, height: string | undefined, space: string | undefined, caption: string | undefined) =>
    `![image](/${settings.jekyllRelativeResourcePath}/${fileName}/${contents.replace(/\s/g, '-')}.${suffix})`
    + `${convertImageSize(width, height)}`
    + `${convertImageCaption(caption)}`;

  return input.replace(ObsidianRegex.ATTACHMENT_LINK, replacer);
};

Comment on lines +18 to +53
// WIP
// TODO: implement this function
export const convertToChirpyV2 = async (plugin: O2Plugin) => {
const settings = plugin.jekyll as JekyllSettings;
const markdownFiles = await copyMarkdownFile(plugin);
for (const file of markdownFiles) {
const fileName = convertFileName(file.name);
const contents: Contents = await plugin.app.vault.read(file);

const filenameConverter = new FilenameConverter();
const result =
convertCurlyBrace(
settings.isEnableCurlyBraceConvertMode,
convertFrontMatter(
convertJekyllResourceLink(
contents,
fileName,
vaultAbsolutePath(plugin),
settings,
),
),
);

await plugin.app.vault.modify(file, result)
.then(() => {
new Notice('Converted to Chirpy successfully.', 5000);
});

// move files to chirpy folder
await moveFiles(
`${vaultAbsolutePath(plugin)}/${settings.readyFolder}`,
settings.targetPath(),
settings.pathReplacer,
)
.then(() => new Notice('Moved files to Chirpy successfully.', 5000));
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement convertToChirpyV2 function and enhance error handling and logging.

The function is currently a WIP and has a TODO comment. Enhance error handling and add more descriptive logging to help in troubleshooting and maintaining the code.

+ try {
  const settings = plugin.jekyll as JekyllSettings;
  const markdownFiles = await copyMarkdownFile(plugin);
  for (const file of markdownFiles) {
    const fileName = convertFileName(file.name);
    const contents: Contents = await plugin.app.vault.read(file);

    const result =
      convertCurlyBrace(
        settings.isEnableCurlyBraceConvertMode,
        convertFrontMatter(
          convertJekyllResourceLink(
            contents,
            fileName,
            vaultAbsolutePath(plugin),
            settings,
          ),
        ),
      );

    await plugin.app.vault.modify(file, result)
      .then(() => {
        new Notice('Converted to Chirpy successfully.', 5000);
      });

    // move files to chirpy folder
    await moveFiles(
      `${vaultAbsolutePath(plugin)}/${settings.readyFolder}`,
      settings.targetPath(),
      settings.pathReplacer,
    )
      .then(() => new Notice('Moved files to Chirpy successfully.', 5000));
  }
+ } catch (error) {
+   console.error('Error during Chirpy conversion:', error);
+   new Notice('Chirpy conversion failed, see console for more information.');
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// WIP
// TODO: implement this function
export const convertToChirpyV2 = async (plugin: O2Plugin) => {
const settings = plugin.jekyll as JekyllSettings;
const markdownFiles = await copyMarkdownFile(plugin);
for (const file of markdownFiles) {
const fileName = convertFileName(file.name);
const contents: Contents = await plugin.app.vault.read(file);
const filenameConverter = new FilenameConverter();
const result =
convertCurlyBrace(
settings.isEnableCurlyBraceConvertMode,
convertFrontMatter(
convertJekyllResourceLink(
contents,
fileName,
vaultAbsolutePath(plugin),
settings,
),
),
);
await plugin.app.vault.modify(file, result)
.then(() => {
new Notice('Converted to Chirpy successfully.', 5000);
});
// move files to chirpy folder
await moveFiles(
`${vaultAbsolutePath(plugin)}/${settings.readyFolder}`,
settings.targetPath(),
settings.pathReplacer,
)
.then(() => new Notice('Moved files to Chirpy successfully.', 5000));
}
};
// WIP
// TODO: implement this function
export const convertToChirpyV2 = async (plugin: O2Plugin) => {
try {
const settings = plugin.jekyll as JekyllSettings;
const markdownFiles = await copyMarkdownFile(plugin);
for (const file of markdownFiles) {
const fileName = convertFileName(file.name);
const contents: Contents = await plugin.app.vault.read(file);
const result =
convertCurlyBrace(
settings.isEnableCurlyBraceConvertMode,
convertFrontMatter(
convertJekyllResourceLink(
contents,
fileName,
vaultAbsolutePath(plugin),
settings,
),
),
);
await plugin.app.vault.modify(file, result)
.then(() => {
new Notice('Converted to Chirpy successfully.', 5000);
});
// move files to chirpy folder
await moveFiles(
`${vaultAbsolutePath(plugin)}/${settings.readyFolder}`,
settings.targetPath(),
settings.pathReplacer,
)
.then(() => new Notice('Moved files to Chirpy successfully.', 5000));
}
} catch (error) {
console.error('Error during Chirpy conversion:', error);
new Notice('Chirpy conversion failed, see console for more information.');
}
};

Comment on lines +9 to +30
const parseFrontMatter = (content: string): [FrontMatter, string] => {
if (!content.startsWith('---')) {
return [{}, content];
}

// for define front matter boundary
const endOfFrontMatter = content.indexOf('---', 3);
if (endOfFrontMatter === -1) {
return [{}, content];
}

const frontMatterLines = content.substring(3, endOfFrontMatter);
const body = content.substring(endOfFrontMatter + 3).trimStart();

try {
const frontMatter = yaml.load(frontMatterLines) as FrontMatter;
return [frontMatter, body];
} catch (e) {
console.error(e);
return [{}, content];
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling in parseFrontMatter.

Provide more context in the error message to make debugging easier.

const parseFrontMatter = (content: string): [FrontMatter, string] => {
  if (!content.startsWith('---')) {
    return [{}, content];
  }

  // for define front matter boundary
  const endOfFrontMatter = content.indexOf('---', 3);
  if (endOfFrontMatter === -1) {
    return [{}, content];
  }

  const frontMatterLines = content.substring(3, endOfFrontMatter);
  const body = content.substring(endOfFrontMatter + 3).trimStart();

  try {
    const frontMatter = yaml.load(frontMatterLines) as FrontMatter;
    return [frontMatter, body];
  } catch (e) {
    console.error(`Error parsing front matter: ${e.message}`);
    return [{}, content];
  }
};

src/FrontMatterConverter.ts Show resolved Hide resolved
src/FrontMatterConverter.ts Show resolved Hide resolved
src/FrontMatterConverter.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between edb0e65 and efa1543.

Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
Files selected for processing (2)
  • .github/workflows/auto-release.yml (2 hunks)
  • .github/workflows/ci.yml (2 hunks)
Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/auto-release.yml
  • .github/workflows/ci.yml

@songkg7 songkg7 merged commit 4c4588f into main Jul 4, 2024
17 of 20 checks passed
@songkg7 songkg7 deleted the feat-docusaurus branch July 4, 2024 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chirpy Related to Jekyll Chirpy theme docusaurus enhancement New feature or request size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FR] support mkdocs-material, docusaurus
2 participants