From f2d1fcdc10130cd107fa38508c37b50136870eeb Mon Sep 17 00:00:00 2001
From: Bryan Mishkin <698306+bmish@users.noreply.github.com>
Date: Sun, 28 Apr 2024 09:49:01 -0400
Subject: [PATCH 1/2] fix: OS-agnostic handling of end-of-line characters
---
lib/config-list.ts | 3 ++-
lib/generator.ts | 11 +++++++----
lib/markdown.ts | 16 ++++++++++------
lib/rule-doc-notices.ts | 3 ++-
lib/rule-list-legend.ts | 3 ++-
lib/rule-list.ts | 7 ++++---
lib/rule-options-list.ts | 3 ++-
lib/string.ts | 4 +++-
8 files changed, 32 insertions(+), 18 deletions(-)
diff --git a/lib/config-list.ts b/lib/config-list.ts
index 23a7ad48..bf8cabca 100644
--- a/lib/config-list.ts
+++ b/lib/config-list.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import {
BEGIN_CONFIG_LIST_MARKER,
END_CONFIG_LIST_MARKER,
@@ -111,5 +112,5 @@ export function updateConfigsList(
ignoreConfig
);
- return `${preList}${BEGIN_CONFIG_LIST_MARKER}\n\n${list}\n\n${END_CONFIG_LIST_MARKER}${postList}`;
+ return `${preList}${BEGIN_CONFIG_LIST_MARKER}${EOL}${EOL}${list}${EOL}${EOL}${END_CONFIG_LIST_MARKER}${postList}`;
}
diff --git a/lib/generator.ts b/lib/generator.ts
index 1d633c55..75c92150 100644
--- a/lib/generator.ts
+++ b/lib/generator.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { getAllNamedOptions, hasOptions } from './rule-options.js';
@@ -168,16 +169,18 @@ export async function generate(path: string, options?: GenerateOptions) {
// The rule doc header will be added later.
let newRuleDocContents = [
ruleDocSectionInclude.length > 0
- ? ruleDocSectionInclude.map((title) => `## ${title}`).join('\n\n')
+ ? ruleDocSectionInclude
+ .map((title) => `## ${title}`)
+ .join(`${EOL}${EOL}`)
: undefined,
ruleHasOptions
- ? `## Options\n\n${BEGIN_RULE_OPTIONS_LIST_MARKER}\n${END_RULE_OPTIONS_LIST_MARKER}`
+ ? `## Options${EOL}${EOL}${BEGIN_RULE_OPTIONS_LIST_MARKER}${EOL}${END_RULE_OPTIONS_LIST_MARKER}`
: undefined,
]
.filter((section) => section !== undefined)
- .join('\n\n');
+ .join(`${EOL}${EOL}`);
if (newRuleDocContents !== '') {
- newRuleDocContents = `\n${newRuleDocContents}\n`;
+ newRuleDocContents = `${EOL}${newRuleDocContents}${EOL}`;
}
mkdirSync(dirname(pathToDoc), { recursive: true });
diff --git a/lib/markdown.ts b/lib/markdown.ts
index 3496a15d..2a72cbac 100644
--- a/lib/markdown.ts
+++ b/lib/markdown.ts
@@ -1,3 +1,5 @@
+import { EOL } from 'node:os';
+
// General helpers for dealing with markdown files / content.
/**
@@ -12,7 +14,7 @@ export function replaceOrCreateHeader(
newHeader: string,
marker: string
) {
- const lines = markdown.split('\n');
+ const lines = markdown.split(EOL);
const titleLineIndex = lines.findIndex((line) => line.startsWith('# '));
const markerLineIndex = lines.indexOf(marker);
@@ -22,16 +24,18 @@ export function replaceOrCreateHeader(
// Any YAML front matter or anything else above the title should be kept as-is ahead of the new header.
const preHeader = lines
.slice(0, Math.max(titleLineIndex, dashesLineIndex2 + 1))
- .join('\n');
+ .join(EOL);
// Anything after the marker comment, title, or YAML front matter should be kept as-is after the new header.
const postHeader = lines
.slice(
Math.max(markerLineIndex + 1, titleLineIndex + 1, dashesLineIndex2 + 1)
)
- .join('\n');
+ .join(EOL);
- return `${preHeader ? `${preHeader}\n` : ''}${newHeader}\n${postHeader}`;
+ return `${
+ preHeader ? `${preHeader}${EOL}` : ''
+ }${newHeader}${EOL}${postHeader}`;
}
/**
@@ -42,7 +46,7 @@ export function findSectionHeader(
str: string
): string | undefined {
// Get all the matching strings.
- const regexp = new RegExp(`## .*${str}.*\n`, 'giu');
+ const regexp = new RegExp(`## .*${str}.*${EOL}`, 'giu');
const sectionPotentialMatches = [...markdown.matchAll(regexp)].map(
(match) => match[0]
);
@@ -64,7 +68,7 @@ export function findSectionHeader(
}
export function findFinalHeaderLevel(str: string) {
- const lines = str.split('\n');
+ const lines = str.split(EOL);
const finalHeader = lines.reverse().find((line) => line.match('^(#+) .+$'));
return finalHeader ? finalHeader.indexOf(' ') : undefined;
}
diff --git a/lib/rule-doc-notices.ts b/lib/rule-doc-notices.ts
index b412d9e9..fabf9626 100644
--- a/lib/rule-doc-notices.ts
+++ b/lib/rule-doc-notices.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import { END_RULE_HEADER_MARKER } from './comment-markers.js';
import {
EMOJI_DEPRECATED,
@@ -523,5 +524,5 @@ export function generateRuleHeaderLines(
),
'',
END_RULE_HEADER_MARKER,
- ].join('\n');
+ ].join(EOL);
}
diff --git a/lib/rule-list-legend.ts b/lib/rule-list-legend.ts
index 18619f96..6b11efa9 100644
--- a/lib/rule-list-legend.ts
+++ b/lib/rule-list-legend.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import {
EMOJI_DEPRECATED,
EMOJI_FIXABLE,
@@ -287,5 +288,5 @@ export function generateLegend(
);
}
- return legends.join('\\\n'); // Back slash ensures these end up displayed on separate lines.
+ return legends.join(`\\${EOL}`); // Back slash ensures these end up displayed on separate lines.
}
diff --git a/lib/rule-list.ts b/lib/rule-list.ts
index 9b5820e6..5f9dccc4 100644
--- a/lib/rule-list.ts
+++ b/lib/rule-list.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import {
BEGIN_RULE_LIST_MARKER,
END_RULE_LIST_MARKER,
@@ -288,7 +289,7 @@ function generateRuleListMarkdownForRulesAndHeaders(
);
}
- return parts.join('\n\n');
+ return parts.join(`${EOL}${EOL}`);
}
/**
@@ -546,7 +547,7 @@ export function updateRulesList(
urlRuleDoc
);
- const newContent = `${legend ? `${legend}\n\n` : ''}${list}`;
+ const newContent = `${legend ? `${legend}${EOL}${EOL}` : ''}${list}`;
- return `${preList}${BEGIN_RULE_LIST_MARKER}\n\n${newContent}\n\n${END_RULE_LIST_MARKER}${postList}`;
+ return `${preList}${BEGIN_RULE_LIST_MARKER}${EOL}${EOL}${newContent}${EOL}${EOL}${END_RULE_LIST_MARKER}${postList}`;
}
diff --git a/lib/rule-options-list.ts b/lib/rule-options-list.ts
index eee4acfb..4339f6cd 100644
--- a/lib/rule-options-list.ts
+++ b/lib/rule-options-list.ts
@@ -1,3 +1,4 @@
+import { EOL } from 'node:os';
import {
BEGIN_RULE_OPTIONS_LIST_MARKER,
END_RULE_OPTIONS_LIST_MARKER,
@@ -159,5 +160,5 @@ export function updateRuleOptionsList(
// New rule options list.
const list = generateRuleOptionsListMarkdown(rule);
- return `${preList}${BEGIN_RULE_OPTIONS_LIST_MARKER}\n\n${list}\n\n${END_RULE_OPTIONS_LIST_MARKER}${postList}`;
+ return `${preList}${BEGIN_RULE_OPTIONS_LIST_MARKER}${EOL}${EOL}${list}${EOL}${EOL}${END_RULE_OPTIONS_LIST_MARKER}${postList}`;
}
diff --git a/lib/string.ts b/lib/string.ts
index 0fada49d..6ae666e0 100644
--- a/lib/string.ts
+++ b/lib/string.ts
@@ -1,3 +1,5 @@
+import { EOL } from 'node:os';
+
export function toSentenceCase(str: string) {
return str.replace(/^\w/u, function (txt) {
return txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase();
@@ -20,7 +22,7 @@ export function capitalizeOnlyFirstLetter(str: string) {
}
function sanitizeMarkdownTableCell(text: string): string {
- return text.replace(/\|/gu, '\\|').replace(/\n/gu, '
');
+ return text.replace(/\|/gu, '\\|').replace(new RegExp(EOL, 'gu'), '
');
}
export function sanitizeMarkdownTable(
From f392f91e0fb6450e6f42f3cbfa45513d0e7afb9a Mon Sep 17 00:00:00 2001
From: Bryan Mishkin <698306+bmish@users.noreply.github.com>
Date: Sun, 28 Apr 2024 10:38:27 -0400
Subject: [PATCH 2/2] try stubbing EOL in test
---
jest.config.cjs | 1 +
test/jest.setup.cjs | 6 ++++++
2 files changed, 7 insertions(+)
create mode 100644 test/jest.setup.cjs
diff --git a/jest.config.cjs b/jest.config.cjs
index f0111b92..34937dd6 100644
--- a/jest.config.cjs
+++ b/jest.config.cjs
@@ -6,6 +6,7 @@ const jestConfig = {
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
testMatch: ['/test/**/*-test.ts'],
+ setupFiles: ['/test/jest.setup.cjs'],
transform: {
'^.+\\.tsx?$': ['ts-jest', { useESM: true }],
},
diff --git a/test/jest.setup.cjs b/test/jest.setup.cjs
new file mode 100644
index 00000000..82c48591
--- /dev/null
+++ b/test/jest.setup.cjs
@@ -0,0 +1,6 @@
+const os = require('node:os');
+const sinon = require('sinon');
+
+module.exports = function () {
+ sinon.stub(os, 'EOL').value('\n'); // Stub os.EOL to always be '\n' for testing/snapshot purposes.
+};