Skip to content

Commit

Permalink
Respect EOL character from .editorconfig (#590)
Browse files Browse the repository at this point in the history
* add .editorconfig support for new line choice

* add more tests and change the markdown path to root

* add full tests inside & document the hypothetical markdown file

---------

Co-authored-by: Bryan Mishkin <[email protected]>
  • Loading branch information
CloudNStoyan and bmish authored Dec 12, 2024
1 parent d998fe2 commit cd94000
Show file tree
Hide file tree
Showing 13 changed files with 376 additions and 11 deletions.
5 changes: 3 additions & 2 deletions lib/config-list.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { EOL } from 'node:os';
import {
BEGIN_CONFIG_LIST_MARKER,
END_CONFIG_LIST_MARKER,
} from './comment-markers.js';
import { markdownTable } from 'markdown-table';
import type { ConfigsToRules, ConfigEmojis, Plugin, Config } from './types.js';
import { ConfigFormat, configNameToDisplay } from './config-format.js';
import { sanitizeMarkdownTable } from './string.js';
import { getEndOfLine, sanitizeMarkdownTable } from './string.js';

const EOL = getEndOfLine();

/**
* Check potential locations for the config description.
Expand Down
4 changes: 3 additions & 1 deletion lib/generator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EOL } from 'node:os';
import { existsSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { getAllNamedOptions, hasOptions } from './rule-options.js';
Expand Down Expand Up @@ -34,6 +33,7 @@ import { OPTION_TYPE, RuleModule } from './types.js';
import { replaceRulePlaceholder } from './rule-link.js';
import { updateRuleOptionsList } from './rule-options-list.js';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { getEndOfLine } from './string.js';

function stringOrArrayWithFallback<T extends string | readonly string[]>(
stringOrArray: undefined | T,
Expand Down Expand Up @@ -63,6 +63,8 @@ function stringOrArrayToArrayWithFallback(

// eslint-disable-next-line complexity
export async function generate(path: string, options?: GenerateOptions) {
const EOL = getEndOfLine();

const plugin = await loadPlugin(path);
const pluginPrefix = await getPluginPrefix(path);
const configsToRules = await resolveConfigsToRules(plugin);
Expand Down
8 changes: 7 additions & 1 deletion lib/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EOL } from 'node:os';
import { getEndOfLine } from './string.js';

// General helpers for dealing with markdown files / content.

Expand All @@ -14,6 +14,8 @@ export function replaceOrCreateHeader(
newHeader: string,
marker: string,
) {
const EOL = getEndOfLine();

const lines = markdown.split(EOL);

const titleLineIndex = lines.findIndex((line) => line.startsWith('# '));
Expand Down Expand Up @@ -45,6 +47,8 @@ export function findSectionHeader(
markdown: string,
str: string,
): string | undefined {
const EOL = getEndOfLine();

// Get all the matching strings.
const regexp = new RegExp(`## .*${str}.*${EOL}`, 'giu');
const sectionPotentialMatches = [...markdown.matchAll(regexp)].map(
Expand All @@ -68,6 +72,8 @@ export function findSectionHeader(
}

export function findFinalHeaderLevel(str: string) {
const EOL = getEndOfLine();

const lines = str.split(EOL);
const finalHeader = lines.reverse().find((line) => line.match('^(#+) .+$'));
return finalHeader ? finalHeader.indexOf(' ') : undefined;
Expand Down
4 changes: 3 additions & 1 deletion lib/rule-doc-notices.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EOL } from 'node:os';
import { END_RULE_HEADER_MARKER } from './comment-markers.js';
import {
EMOJI_DEPRECATED,
Expand Down Expand Up @@ -27,9 +26,12 @@ import {
toSentenceCase,
removeTrailingPeriod,
addTrailingPeriod,
getEndOfLine,
} from './string.js';
import { ConfigFormat, configNameToDisplay } from './config-format.js';

const EOL = getEndOfLine();

function severityToTerminology(severity: SEVERITY_TYPE) {
switch (severity) {
case SEVERITY_TYPE.error: {
Expand Down
4 changes: 3 additions & 1 deletion lib/rule-list-legend.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EOL } from 'node:os';
import {
EMOJI_DEPRECATED,
EMOJI_FIXABLE,
Expand All @@ -18,6 +17,9 @@ import {
} from './types.js';
import { RULE_TYPE_MESSAGES_LEGEND, RULE_TYPES } from './rule-type.js';
import { ConfigFormat, configNameToDisplay } from './config-format.js';
import { getEndOfLine } from './string.js';

const EOL = getEndOfLine();

export const SEVERITY_TYPE_TO_WORD: {
[key in SEVERITY_TYPE]: string;
Expand Down
11 changes: 9 additions & 2 deletions lib/rule-list.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EOL } from 'node:os';
import {
BEGIN_RULE_LIST_MARKER,
END_RULE_LIST_MARKER,
Expand Down Expand Up @@ -34,7 +33,11 @@ import type {
import { EMOJIS_TYPE } from './rule-type.js';
import { hasOptions } from './rule-options.js';
import { getLinkToRule } from './rule-link.js';
import { capitalizeOnlyFirstLetter, sanitizeMarkdownTable } from './string.js';
import {
capitalizeOnlyFirstLetter,
getEndOfLine,
sanitizeMarkdownTable,
} from './string.js';
import { noCase } from 'change-case';
import { getProperty } from 'dot-prop';
import { boolean, isBooleanable } from 'boolean';
Expand Down Expand Up @@ -269,6 +272,8 @@ function generateRuleListMarkdownForRulesAndHeaders(
ignoreConfig: readonly string[],
urlRuleDoc?: string | UrlRuleDocFunction,
): string {
const EOL = getEndOfLine();

const parts: string[] = [];

for (const { title, rules } of rulesAndHeaders) {
Expand Down Expand Up @@ -416,6 +421,8 @@ export function updateRulesList(
urlConfigs?: string,
urlRuleDoc?: string | UrlRuleDocFunction,
): string {
const EOL = getEndOfLine();

let listStartIndex = markdown.indexOf(BEGIN_RULE_LIST_MARKER);
let listEndIndex = markdown.indexOf(END_RULE_LIST_MARKER);

Expand Down
5 changes: 3 additions & 2 deletions lib/rule-options-list.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { EOL } from 'node:os';
import {
BEGIN_RULE_OPTIONS_LIST_MARKER,
END_RULE_OPTIONS_LIST_MARKER,
} from './comment-markers.js';
import { markdownTable } from 'markdown-table';
import type { RuleModule } from './types.js';
import { RuleOption, getAllNamedOptions } from './rule-options.js';
import { sanitizeMarkdownTable } from './string.js';
import { getEndOfLine, sanitizeMarkdownTable } from './string.js';

const EOL = getEndOfLine();

export enum COLUMN_TYPE {
// Alphabetical order.
Expand Down
24 changes: 23 additions & 1 deletion lib/string.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { EOL } from 'node:os';
import editorconfig from 'editorconfig';

const endOfLine = getEndOfLine();

export function toSentenceCase(str: string) {
return str.replace(/^\w/u, function (txt) {
Expand All @@ -24,11 +27,30 @@ export function capitalizeOnlyFirstLetter(str: string) {
function sanitizeMarkdownTableCell(text: string): string {
return text
.replaceAll('|', String.raw`\|`)
.replaceAll(new RegExp(EOL, 'gu'), '<br/>');
.replaceAll(new RegExp(endOfLine, 'gu'), '<br/>');
}

export function sanitizeMarkdownTable(
text: readonly (readonly string[])[],
): readonly (readonly string[])[] {
return text.map((row) => row.map((col) => sanitizeMarkdownTableCell(col)));
}

// Gets the end of line string while respecting the
// `.editorconfig` and falling back to `EOL` from `node:os`.
export function getEndOfLine() {
// The passed `markdown.md` argument is used as an example
// of a markdown file in the plugin root folder in order to
// check for any specific markdown configurations.
const config = editorconfig.parseSync('markdown.md');

let endOfLine = EOL;

if (config.end_of_line === 'lf') {
endOfLine = '\n';
} else if (config.end_of_line === 'crlf') {
endOfLine = '\r\n';
}

return endOfLine;
}
45 changes: 45 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"cosmiconfig": "^9.0.0",
"deepmerge": "^4.2.2",
"dot-prop": "^9.0.0",
"editorconfig": "^2.0.0",
"jest-diff": "^29.2.1",
"json-schema": "^0.4.0",
"json-schema-traverse": "^1.0.0",
Expand Down
118 changes: 118 additions & 0 deletions test/lib/generate/__snapshots__/editor-config-test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using crlf end of line from .editorconfig 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
💼 Configurations enabled in.
| Name | 💼 |
| :------------------- | :------------------------------------- |
| [a](docs/rules/a.md) | ![badge-a][] ![badge-B][] ![badge-c][] |
| [B](docs/rules/B.md) | |
| [c](docs/rules/c.md) | |
<!-- end auto-generated rules list -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using crlf end of line from .editorconfig 2`] = `
"# test/a
💼 This rule is enabled in the following configs: \`a\`, \`B\`, \`c\`.
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using crlf end of line from .editorconfig 3`] = `
"# test/B
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using crlf end of line from .editorconfig 4`] = `
"# test/c
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using lf end of line from .editorconfig 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
💼 Configurations enabled in.
| Name | 💼 |
| :------------------- | :------------------------------------- |
| [a](docs/rules/a.md) | ![badge-a][] ![badge-B][] ![badge-c][] |
| [B](docs/rules/B.md) | |
| [c](docs/rules/c.md) | |
<!-- end auto-generated rules list -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using lf end of line from .editorconfig 2`] = `
"# test/a
💼 This rule is enabled in the following configs: \`a\`, \`B\`, \`c\`.
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using lf end of line from .editorconfig 3`] = `
"# test/B
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using lf end of line from .editorconfig 4`] = `
"# test/c
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using the end of line from .editorconfig while respecting the .md specific end of line setting 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
💼 Configurations enabled in.
| Name | 💼 |
| :------------------- | :------------------------------------- |
| [a](docs/rules/a.md) | ![badge-a][] ![badge-B][] ![badge-c][] |
| [B](docs/rules/B.md) | |
| [c](docs/rules/c.md) | |
<!-- end auto-generated rules list -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using the end of line from .editorconfig while respecting the .md specific end of line setting 2`] = `
"# test/a
💼 This rule is enabled in the following configs: \`a\`, \`B\`, \`c\`.
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using the end of line from .editorconfig while respecting the .md specific end of line setting 3`] = `
"# test/B
<!-- end auto-generated rule header -->
"
`;

exports[`string (getEndOfLine) generates using the correct end of line when .editorconfig exists generates using the end of line from .editorconfig while respecting the .md specific end of line setting 4`] = `
"# test/c
<!-- end auto-generated rule header -->
"
`;
Loading

0 comments on commit cd94000

Please sign in to comment.