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

Publish multiple files in one commits #9

Open
wants to merge 8 commits into
base: single-commit
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 11 additions & 56 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,84 +238,39 @@ export default class DigitalGarden extends Plugin {
imagesToDelete.length,
);

let errorFiles = 0;
let errorDeleteFiles = 0;
let errorDeleteImage = 0;

new Notice(
`Publishing ${filesToPublish.length} notes, deleting ${filesToDelete.length} notes and ${imagesToDelete.length} images. See the status bar in lower right corner for progress.`,
8000,
);

for (const file of filesToPublish) {
try {
statusBar.increment();
await publisher.publish(file);
} catch {
errorFiles++;
await publisher.publishBatch(filesToPublish);
statusBar.incrementMultiple(filesToPublish.length);

new Notice(
`Unable to publish note ${file.file.name}, skipping it.`,
);
}
for (const file of filesToDelete) {
await publisher.deleteNote(file.path);
statusBar.increment();
}

for (const filePath of filesToDelete) {
try {
statusBar.increment();

// TODO: include sha from file.remoteHash to make faster!
await publisher.deleteNote(
filePath.path,
filePath.sha,
);
} catch {
errorDeleteFiles++;

new Notice(
`Unable to delete note ${filePath}, skipping it.`,
);
}
}

for (const filePath of imagesToDelete) {
try {
statusBar.increment();

await publisher.deleteImage(
filePath.path,
filePath.sha,
);
} catch {
errorDeleteImage++;

new Notice(
`Unable to delete image ${filePath}, skipping it.`,
);
}
for (const image of imagesToDelete) {
await publisher.deleteImage(image.path);
statusBar.increment();
}

statusBar.finish(8000);

new Notice(
`Successfully published ${
filesToPublish.length - errorFiles
} notes to your garden.`,
`Successfully published ${filesToPublish.length} notes to your garden.`,
);

if (filesToDelete.length > 0) {
new Notice(
`Successfully deleted ${
filesToDelete.length - errorDeleteFiles
} notes from your garden.`,
`Successfully deleted ${filesToDelete.length} notes from your garden.`,
);
}

if (imagesToDelete.length > 0) {
new Notice(
`Successfully deleted ${
imagesToDelete.length - errorDeleteImage
} images from your garden.`,
`Successfully deleted ${imagesToDelete.length} images from your garden.`,
);
}
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion manifest-beta.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "digitalgarden",
"name": "Digital Garden",
"version": "2.56.2",
"version": "2.57.2",
"minAppVersion": "0.12.0",
"description": "Publish your notes to the web for others to enjoy. For free.",
"author": "Ole Eskild Steensen",
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "digitalgarden",
"name": "Digital Garden",
"version": "2.56.2",
"version": "2.57.2",
"minAppVersion": "0.12.0",
"description": "Publish your notes to the web for others to enjoy. For free.",
"author": "Ole Eskild Steensen",
Expand Down
17 changes: 2 additions & 15 deletions src/compiler/GardenPageCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import Logger from "js-logger";
import { DataviewCompiler } from "./DataviewCompiler";
import { PublishFile } from "../publishFile/PublishFile";
import { replaceBlockIDs } from "./replaceBlockIDs";

export interface Asset {
path: string;
Expand Down Expand Up @@ -144,21 +145,7 @@ export class GardenPageCompiler {
};

createBlockIDs: TCompilerStep = () => (text: string) => {
const block_pattern = / \^([\w\d-]+)/g;
const complex_block_pattern = /\n\^([\w\d-]+)\n/g;

text = text.replace(
complex_block_pattern,
(_match: string, $1: string) => {
return `{ #${$1}}\n\n`;
},
);

text = text.replace(block_pattern, (match: string, $1: string) => {
return `\n{ #${$1}}\n`;
});

return text;
return replaceBlockIDs(text);
};

removeObsidianComments: TCompilerStep = () => (text) => {
Expand Down
65 changes: 65 additions & 0 deletions src/compiler/createBlockIDs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { replaceBlockIDs } from "./replaceBlockIDs";

describe("replaceBlockIDs", () => {
it("should replace block IDs in markdown", () => {
const EXPECTED_MARKDOWN = `
# header

foo ^block-id-1234

bar ^block-id-5678
`;

const result = replaceBlockIDs(EXPECTED_MARKDOWN);

expect(result).toBe(`
# header

foo
{ #block-id-1234}


bar
{ #block-id-5678}

`);
});

it("should not replace block IDs in code blocks", () => {
const CODEBLOCK_WITH_BLOCKIDS = `
\`\`\`
foobar.
this is a code block.
but it contains a block ID to try to fool the compiler
and, consequently, wreck your garden.
here it goes: ^block-id-1234
and for fun, here's another: ^block-id-5678
\`\`\`

additionally, block IDs outside of code blocks should be replaced
for example, ^block-id-9999
and ^block-id-0000
`;

const result = replaceBlockIDs(CODEBLOCK_WITH_BLOCKIDS);

expect(result).toBe(`
\`\`\`
foobar.
this is a code block.
but it contains a block ID to try to fool the compiler
and, consequently, wreck your garden.
here it goes: ^block-id-1234
and for fun, here's another: ^block-id-5678
\`\`\`

additionally, block IDs outside of code blocks should be replaced
for example,
{ #block-id-9999}

and
{ #block-id-0000}

`);
});
});
35 changes: 35 additions & 0 deletions src/compiler/replaceBlockIDs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export function replaceBlockIDs(markdown: string) {
const block_pattern = / \^([\w\d-]+)/g;
const complex_block_pattern = /\n\^([\w\d-]+)\n/g;

// To ensure code blocks are not modified...
const codeBlockPattern = /```[\s\S]*?```/g;

// Extract code blocks and replace them with placeholders
const codeBlocks: string[] = [];

markdown = markdown.replace(codeBlockPattern, (match) => {
codeBlocks.push(match);

return `{{CODE_BLOCK_${codeBlocks.length - 1}}}`;
});

// Replace patterns outside code blocks
markdown = markdown.replace(
complex_block_pattern,
(_match: string, $1: string) => {
return `{ #${$1}}\n\n`;
},
);

markdown = markdown.replace(block_pattern, (_match: string, $1: string) => {
return `\n{ #${$1}}\n`;
});

// Reinsert code blocks
codeBlocks.forEach((block, index) => {
markdown = markdown.replace(`{{CODE_BLOCK_${index}}}`, block);
});

return markdown;
}
26 changes: 26 additions & 0 deletions src/dg-testVault/015 Code blocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
dg-publish: true
---
These codeblocks should not be modified upon publish.

Sample 1
```jinja2
{{ highlight_text }}{% if highlight_location and highlight_location_url %} ([via]({{highlight_location_url}})){% elif highlight_location %} ({{highlight_location}}){% endif %} ^rwhi{{highlight_id}}
{% if highlight_note %}
{{ highlight_note }} ^rwhi{{highlight_id}}-note
{% endif %}
```

Sample 2
```md
In medieval Latin a florilegium (plural florilegia) was a compilation of excerpts from other writings.
The word is from the Latin flos (flower) and legere (to gather): literally a gathering of flowers, or collection of fine extracts from the body of a larger work. ([via](https://en.wikipedia.org/wiki/Florilegium)) ^rwhi724352030
```

Sample 3
```
This codeblock has a transclusion syntax in it.
Check it out: ![[001 Links]]
```

And for sanity, here's some block references outside of code blocks: foobar ^test-123
60 changes: 54 additions & 6 deletions src/publisher/Publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class Publisher {
return await this.delete(path, sha);
}
/** If provided with sha, garden connection does not need to get it seperately! */
async delete(path: string, sha?: string): Promise<boolean> {
public async delete(path: string, sha?: string): Promise<boolean> {
this.validateSettings();

const userGardenConnection = new RepositoryConnection({
Expand All @@ -115,7 +115,7 @@ export default class Publisher {
return !!deleted;
}

async publish(file: CompiledPublishFile): Promise<boolean> {
public async publish(file: CompiledPublishFile): Promise<boolean> {
if (!isPublishFrontmatterValid(file.frontmatter)) {
return false;
}
Expand All @@ -133,7 +133,55 @@ export default class Publisher {
}
}

async uploadToGithub(
public async deleteBatch(filePaths: string[]): Promise<boolean> {
if (filePaths.length === 0) {
return true;
}

try {
const userGardenConnection = new RepositoryConnection({
gardenRepository: this.settings.githubRepo,
githubUserName: this.settings.githubUserName,
githubToken: this.settings.githubToken,
});

await userGardenConnection.deleteFiles(filePaths);

return true;
} catch (error) {
console.error(error);

return false;
}
}

public async publishBatch(files: CompiledPublishFile[]): Promise<boolean> {
const filesToPublish = files.filter((f) =>
isPublishFrontmatterValid(f.frontmatter),
);

if (filesToPublish.length === 0) {
return true;
}

try {
const userGardenConnection = new RepositoryConnection({
gardenRepository: this.settings.githubRepo,
githubUserName: this.settings.githubUserName,
githubToken: this.settings.githubToken,
});

await userGardenConnection.updateFiles(filesToPublish);

return true;
} catch (error) {
console.error(error);

return false;
}
}

private async uploadToGithub(
path: string,
content: string,
remoteFileHash?: string,
Expand Down Expand Up @@ -167,18 +215,18 @@ export default class Publisher {
});
}

async uploadText(filePath: string, content: string, sha?: string) {
private async uploadText(filePath: string, content: string, sha?: string) {
content = Base64.encode(content);
const path = `${NOTE_PATH_BASE}${filePath}`;
await this.uploadToGithub(path, content, sha);
}

async uploadImage(filePath: string, content: string, sha?: string) {
private async uploadImage(filePath: string, content: string, sha?: string) {
const path = `src/site${filePath}`;
await this.uploadToGithub(path, content, sha);
}

async uploadAssets(assets: Assets) {
private async uploadAssets(assets: Assets) {
for (let idx = 0; idx < assets.images.length; idx++) {
const image = assets.images[idx];
await this.uploadImage(image.path, image.content, image.remoteHash);
Expand Down
Loading