Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
}
},
"rules": {
"complexity": ["error", { "max": 10 }],
"import/no-cycle": "error",
"max-depth": ["error", { "max": 2 }],
"max-lines": ["error", { "max": 500 }],
"max-params": ["error", { "max": 3 }],
"typescript/await-thenable": "error",
"typescript/no-floating-promises": "error",
"typescript/no-misused-promises": "error"
Expand Down
30 changes: 18 additions & 12 deletions src/data/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,27 +137,27 @@ function transformOutsideFences(markdown: string, transform: (prose: string) =>
return restoreFencedBlocks(transform(protectedMarkdown), fencedBlocks);
}

type Fence = { character: string; length: number; lines: string[] };

function protectFencedBlocks(markdown: string, fencedBlocks: string[]): string {
const lines = markdown.split("\n");
const protectedLines: string[] = [];
let fence: { character: string; length: number; lines: string[] } | undefined;
let fence: Fence | undefined;

for (const line of lines) {
const marker = /^(?:\s*)(`{3,}|~{3,})/u.exec(line)?.[1];
if (fence === undefined && marker !== undefined) {
const marker = fence === undefined ? /^(?:\s*)(`{3,}|~{3,})/u.exec(line)?.[1] : undefined;
if (marker !== undefined) {
fence = { character: marker[0], length: marker.length, lines: [line] };
continue;
}
if (fence !== undefined) {
fence.lines.push(line);
const closing = /^ {0,3}(`{3,}|~{3,})[ \t]*\r?$/u.exec(line)?.[1];
if (closing?.[0] === fence.character && closing.length >= fence.length) {
protectedLines.push(storeFencedBlock(fence.lines, fencedBlocks));
fence = undefined;
}
if (fence === undefined) {
protectedLines.push(line);
continue;
}
protectedLines.push(line);
fence.lines.push(line);
if (!closesFence(line, fence)) continue;
protectedLines.push(storeFencedBlock(fence.lines, fencedBlocks));
fence = undefined;
}

if (fence !== undefined) {
Expand All @@ -166,6 +166,11 @@ function protectFencedBlocks(markdown: string, fencedBlocks: string[]): string {
return protectedLines.join("\n");
}

function closesFence(line: string, fence: Fence): boolean {
const closing = /^ {0,3}(`{3,}|~{3,})[ \t]*\r?$/u.exec(line)?.[1];
return closing?.[0] === fence.character && closing.length >= fence.length;
}

function storeFencedBlock(lines: string[], fencedBlocks: string[]): string {
const index = fencedBlocks.push(lines.join("\n")) - 1;
return `@@FUREDEA_FENCED_BLOCK_${index}@@`;
Expand All @@ -180,7 +185,8 @@ function restoreFencedBlocks(markdown: string, fencedBlocks: string[]): string {
function convertCaptions(markdown: string): string {
return markdown.replace(
/^!\[([^\]]*)\]\(([^)\s]+)\)\n([_*])(.+)\3$/gmu,
(_match, alt: string, url: string, _delimiter: string, caption: string) => {
(_match, ...captures: string[]) => {
const [alt, url, , caption] = captures;
return [
"<figure>",
`<img src="${escapeHtml(url)}" alt="${escapeHtml(alt)}">`,
Expand Down
34 changes: 34 additions & 0 deletions src/integrations/esa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,40 @@ test.each([0, -1, 1, 1.5, "2"])(
},
);

test.each([
["name", 1],
["body_md", null],
["tags", [1]],
["category", 1],
["wip", "false"],
])("refuses to publish when a searched post has an invalid %s", async (field, value) => {
const methods: string[] = [];
const fetcher: typeof fetch = async (input, options) => {
methods.push(new Request(input, options).method);
return Response.json({
posts: [
{
number: 42,
url: "https://example-team.esa.io/posts/42",
...PAYLOAD.post,
[field]: value,
},
],
});
};

await expect(
upsertEsaPost({
team: "example-team",
accessToken: ACCESS_TOKEN,
canonicalUrl: "https://furedea.com/ja/blog/article-publishing/",
payload: PAYLOAD,
fetcher,
}),
).rejects.toThrow("esa API returned an invalid post search response.");
expect(methods.filter((method) => method !== "GET")).toEqual([]);
});

test("does not publish when a later search page fails", async () => {
const methods: string[] = [];
const fetcher: typeof fetch = async (input, options) => {
Expand Down
12 changes: 10 additions & 2 deletions src/integrations/esa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,20 @@ function isEsaPostSearchResult(value: unknown): value is EsaPostSearchResult {
}

function isEsaPostSummary(value: unknown): value is EsaPostSummary {
return isEsaPostResult(value) && hasEsaPostContent(value) && hasEsaPostMetadata(value);
}

function hasEsaPostContent(value: object): boolean {
return (
isEsaPostResult(value) &&
"name" in value &&
typeof value.name === "string" &&
"body_md" in value &&
typeof value.body_md === "string" &&
typeof value.body_md === "string"
);
}

function hasEsaPostMetadata(value: object): boolean {
return (
"tags" in value &&
Array.isArray(value.tags) &&
value.tags.every((tag) => typeof tag === "string") &&
Expand Down
8 changes: 3 additions & 5 deletions tests/performance/performance_report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ function renderRow(entry: PerformanceReportEntry): string {
(value) => value.toFixed(3),
),
unitWithRange(
metrics.blockingTime,
samples.map(({ blockingTime }) => blockingTime),
{ median: metrics.blockingTime, samples: samples.map(({ blockingTime }) => blockingTime) },
"ms",
Math.round,
),
Expand All @@ -89,12 +88,11 @@ function seconds(milliseconds: number): string {
}

function secondsWithRange(median: number, samples: number[]): string {
return unitWithRange(median, samples, "s", seconds);
return unitWithRange({ median, samples }, "s", seconds);
}

function unitWithRange(
median: number,
samples: number[],
{ median, samples }: { median: number; samples: number[] },
unit: string,
format: (value: number) => string | number,
): string {
Expand Down
Loading