Skip to content

Commit 4852197

Browse files
authored
fix: OS-agnostic handling of end-of-line characters (#524)
* fix: OS-agnostic handling of end-of-line characters * try stubbing EOL in test
1 parent 01c3179 commit 4852197

10 files changed

+39
-18
lines changed

jest.config.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const jestConfig = {
66
preset: 'ts-jest/presets/default-esm',
77
testEnvironment: 'node',
88
testMatch: ['<rootDir>/test/**/*-test.ts'],
9+
setupFiles: ['<rootDir>/test/jest.setup.cjs'],
910
transform: {
1011
'^.+\\.tsx?$': ['ts-jest', { useESM: true }],
1112
},

lib/config-list.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import {
23
BEGIN_CONFIG_LIST_MARKER,
34
END_CONFIG_LIST_MARKER,
@@ -111,5 +112,5 @@ export function updateConfigsList(
111112
ignoreConfig
112113
);
113114

114-
return `${preList}${BEGIN_CONFIG_LIST_MARKER}\n\n${list}\n\n${END_CONFIG_LIST_MARKER}${postList}`;
115+
return `${preList}${BEGIN_CONFIG_LIST_MARKER}${EOL}${EOL}${list}${EOL}${EOL}${END_CONFIG_LIST_MARKER}${postList}`;
115116
}

lib/generator.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
23
import { dirname, join, relative, resolve } from 'node:path';
34
import { getAllNamedOptions, hasOptions } from './rule-options.js';
@@ -168,16 +169,18 @@ export async function generate(path: string, options?: GenerateOptions) {
168169
// The rule doc header will be added later.
169170
let newRuleDocContents = [
170171
ruleDocSectionInclude.length > 0
171-
? ruleDocSectionInclude.map((title) => `## ${title}`).join('\n\n')
172+
? ruleDocSectionInclude
173+
.map((title) => `## ${title}`)
174+
.join(`${EOL}${EOL}`)
172175
: undefined,
173176
ruleHasOptions
174-
? `## Options\n\n${BEGIN_RULE_OPTIONS_LIST_MARKER}\n${END_RULE_OPTIONS_LIST_MARKER}`
177+
? `## Options${EOL}${EOL}${BEGIN_RULE_OPTIONS_LIST_MARKER}${EOL}${END_RULE_OPTIONS_LIST_MARKER}`
175178
: undefined,
176179
]
177180
.filter((section) => section !== undefined)
178-
.join('\n\n');
181+
.join(`${EOL}${EOL}`);
179182
if (newRuleDocContents !== '') {
180-
newRuleDocContents = `\n${newRuleDocContents}\n`;
183+
newRuleDocContents = `${EOL}${newRuleDocContents}${EOL}`;
181184
}
182185

183186
mkdirSync(dirname(pathToDoc), { recursive: true });

lib/markdown.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { EOL } from 'node:os';
2+
13
// General helpers for dealing with markdown files / content.
24

35
/**
@@ -12,7 +14,7 @@ export function replaceOrCreateHeader(
1214
newHeader: string,
1315
marker: string
1416
) {
15-
const lines = markdown.split('\n');
17+
const lines = markdown.split(EOL);
1618

1719
const titleLineIndex = lines.findIndex((line) => line.startsWith('# '));
1820
const markerLineIndex = lines.indexOf(marker);
@@ -22,16 +24,18 @@ export function replaceOrCreateHeader(
2224
// Any YAML front matter or anything else above the title should be kept as-is ahead of the new header.
2325
const preHeader = lines
2426
.slice(0, Math.max(titleLineIndex, dashesLineIndex2 + 1))
25-
.join('\n');
27+
.join(EOL);
2628

2729
// Anything after the marker comment, title, or YAML front matter should be kept as-is after the new header.
2830
const postHeader = lines
2931
.slice(
3032
Math.max(markerLineIndex + 1, titleLineIndex + 1, dashesLineIndex2 + 1)
3133
)
32-
.join('\n');
34+
.join(EOL);
3335

34-
return `${preHeader ? `${preHeader}\n` : ''}${newHeader}\n${postHeader}`;
36+
return `${
37+
preHeader ? `${preHeader}${EOL}` : ''
38+
}${newHeader}${EOL}${postHeader}`;
3539
}
3640

3741
/**
@@ -42,7 +46,7 @@ export function findSectionHeader(
4246
str: string
4347
): string | undefined {
4448
// Get all the matching strings.
45-
const regexp = new RegExp(`## .*${str}.*\n`, 'giu');
49+
const regexp = new RegExp(`## .*${str}.*${EOL}`, 'giu');
4650
const sectionPotentialMatches = [...markdown.matchAll(regexp)].map(
4751
(match) => match[0]
4852
);
@@ -64,7 +68,7 @@ export function findSectionHeader(
6468
}
6569

6670
export function findFinalHeaderLevel(str: string) {
67-
const lines = str.split('\n');
71+
const lines = str.split(EOL);
6872
const finalHeader = lines.reverse().find((line) => line.match('^(#+) .+$'));
6973
return finalHeader ? finalHeader.indexOf(' ') : undefined;
7074
}

lib/rule-doc-notices.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import { END_RULE_HEADER_MARKER } from './comment-markers.js';
23
import {
34
EMOJI_DEPRECATED,
@@ -523,5 +524,5 @@ export function generateRuleHeaderLines(
523524
),
524525
'',
525526
END_RULE_HEADER_MARKER,
526-
].join('\n');
527+
].join(EOL);
527528
}

lib/rule-list-legend.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import {
23
EMOJI_DEPRECATED,
34
EMOJI_FIXABLE,
@@ -287,5 +288,5 @@ export function generateLegend(
287288
);
288289
}
289290

290-
return legends.join('\\\n'); // Back slash ensures these end up displayed on separate lines.
291+
return legends.join(`\\${EOL}`); // Back slash ensures these end up displayed on separate lines.
291292
}

lib/rule-list.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import {
23
BEGIN_RULE_LIST_MARKER,
34
END_RULE_LIST_MARKER,
@@ -288,7 +289,7 @@ function generateRuleListMarkdownForRulesAndHeaders(
288289
);
289290
}
290291

291-
return parts.join('\n\n');
292+
return parts.join(`${EOL}${EOL}`);
292293
}
293294

294295
/**
@@ -546,7 +547,7 @@ export function updateRulesList(
546547
urlRuleDoc
547548
);
548549

549-
const newContent = `${legend ? `${legend}\n\n` : ''}${list}`;
550+
const newContent = `${legend ? `${legend}${EOL}${EOL}` : ''}${list}`;
550551

551-
return `${preList}${BEGIN_RULE_LIST_MARKER}\n\n${newContent}\n\n${END_RULE_LIST_MARKER}${postList}`;
552+
return `${preList}${BEGIN_RULE_LIST_MARKER}${EOL}${EOL}${newContent}${EOL}${EOL}${END_RULE_LIST_MARKER}${postList}`;
552553
}

lib/rule-options-list.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EOL } from 'node:os';
12
import {
23
BEGIN_RULE_OPTIONS_LIST_MARKER,
34
END_RULE_OPTIONS_LIST_MARKER,
@@ -159,5 +160,5 @@ export function updateRuleOptionsList(
159160
// New rule options list.
160161
const list = generateRuleOptionsListMarkdown(rule);
161162

162-
return `${preList}${BEGIN_RULE_OPTIONS_LIST_MARKER}\n\n${list}\n\n${END_RULE_OPTIONS_LIST_MARKER}${postList}`;
163+
return `${preList}${BEGIN_RULE_OPTIONS_LIST_MARKER}${EOL}${EOL}${list}${EOL}${EOL}${END_RULE_OPTIONS_LIST_MARKER}${postList}`;
163164
}

lib/string.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { EOL } from 'node:os';
2+
13
export function toSentenceCase(str: string) {
24
return str.replace(/^\w/u, function (txt) {
35
return txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase();
@@ -20,7 +22,7 @@ export function capitalizeOnlyFirstLetter(str: string) {
2022
}
2123

2224
function sanitizeMarkdownTableCell(text: string): string {
23-
return text.replace(/\|/gu, '\\|').replace(/\n/gu, '<br/>');
25+
return text.replace(/\|/gu, '\\|').replace(new RegExp(EOL, 'gu'), '<br/>');
2426
}
2527

2628
export function sanitizeMarkdownTable(

test/jest.setup.cjs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const os = require('node:os');
2+
const sinon = require('sinon');
3+
4+
module.exports = function () {
5+
sinon.stub(os, 'EOL').value('\n'); // Stub os.EOL to always be '\n' for testing/snapshot purposes.
6+
};

0 commit comments

Comments
 (0)