Skip to content

Commit

Permalink
feat: allow content above rule doc title
Browse files Browse the repository at this point in the history
  • Loading branch information
bmish committed Nov 23, 2022
1 parent 1a6850f commit 08a1ed2
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 32 deletions.
15 changes: 7 additions & 8 deletions lib/generator.ts
Expand Up @@ -162,9 +162,8 @@ export async function generate(path: string, options?: GenerateOptions) {
(details) => !ignoreDeprecatedRules || !details.deprecated
);

let initializedRuleDoc = false;

// Update rule doc for each rule.
let initializedRuleDoc = false;
for (const { name, description, schema } of details) {
const pathToDoc = join(path, pathRuleDoc).replace(/{name}/g, name);

Expand Down Expand Up @@ -243,6 +242,12 @@ export async function generate(path: string, options?: GenerateOptions) {
}
}

if (initRuleDocs && !initializedRuleDoc) {
throw new Error(
'--init-rule-docs was enabled, but no rule doc file needed to be created.'
);
}

// Find the README.
const pathToReadme = getPathWithExactFileNameCasing(join(path, pathRuleList));
if (!pathToReadme || !existsSync(pathToReadme)) {
Expand Down Expand Up @@ -282,10 +287,4 @@ export async function generate(path: string, options?: GenerateOptions) {
} else {
writeFileSync(pathToReadme, readmeContentsNew, 'utf8');
}

if (initRuleDocs && !initializedRuleDoc) {
throw new Error(
'--init-rule-docs was enabled, but no rule doc file needed to be created.'
);
}
}
32 changes: 27 additions & 5 deletions lib/markdown.ts
Expand Up @@ -14,14 +14,36 @@ export function replaceOrCreateHeader(
) {
const lines = markdown.split('\n');

const titleLineIndex = lines.findIndex((line) => line.startsWith('# '));
const markerLineIndex = lines.indexOf(marker);
const dashesLineIndex1 = lines.indexOf('---');
const dashesLineIndex2 = lines.indexOf('---', dashesLineIndex1 + 1);
const hasTitle = titleLineIndex !== -1;
const hasMarker = markerLineIndex !== -1;
const hasYamlFrontMatter = dashesLineIndex1 === 0 && dashesLineIndex2 !== -1;

if (markerLineIndex === -1 && lines.length > 0 && lines[0].startsWith('# ')) {
// No marker present so delete any existing title before we add the new one.
lines.splice(0, 1);
}
// 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,
hasTitle ? titleLineIndex : hasYamlFrontMatter ? dashesLineIndex2 + 1 : 0
)
.join('\n');

// Anything after the marker comment, title, or YAML front matter should be kept as-is after the new header.
const postHeader = lines
.slice(
hasMarker
? markerLineIndex + 1
: hasTitle
? titleLineIndex + 1
: hasYamlFrontMatter
? dashesLineIndex2 + 1
: 0
)
.join('\n');

return `${newHeader}\n${lines.slice(markerLineIndex + 1).join('\n')}`;
return `${preHeader ? `${preHeader}\n` : ''}${newHeader}\n${postHeader}`;
}

/**
Expand Down
51 changes: 51 additions & 0 deletions test/lib/generate/__snapshots__/comment-markers-test.ts.snap
Expand Up @@ -46,6 +46,24 @@ exports[`generate (comment markers) no existing comment markers - minimal doc co
"
`;

exports[`generate (comment markers) no existing comment markers - rule doc with YAML-formatted metadata (front matter) above title updates the documentation 1`] = `
"---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
# Description (\`test/no-foo\`)
💼 This rule is enabled in the ✅ \`recommended\` config.
<!-- end auto-generated rule header -->
Pre-existing notice about the rule being recommended.
## Rule details
Details."
`;

exports[`generate (comment markers) no existing comment markers - with no blank lines in existing content generates the documentation 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
Expand Down Expand Up @@ -94,6 +112,39 @@ exports[`generate (comment markers) no existing comment markers - with one blank
Existing rule doc content."
`;

exports[`generate (comment markers) rule doc with YAML-formatted metadata (front matter) above title and comment marker updates the documentation 1`] = `
"---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
# Description (\`test/no-foo\`)
💼 This rule is enabled in the ✅ \`recommended\` config.
<!-- end auto-generated rule header -->
## Rule details
Details."
`;

exports[`generate (comment markers) rule doc with YAML-formatted metadata (front matter) and nothing else updates the documentation 1`] = `
"---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
# Description (\`test/no-foo\`)
💼 This rule is enabled in the ✅ \`recommended\` config.
<!-- end auto-generated rule header -->
"
`;

exports[`generate (comment markers) rule doc without header marker but pre-existing header updates the documentation 1`] = `
"# Description (\`test/no-foo\`)
Expand Down
159 changes: 146 additions & 13 deletions test/lib/generate/comment-markers-test.ts
Expand Up @@ -260,6 +260,55 @@ describe('generate (comment markers)', function () {
});
});

describe('no existing comment markers - rule doc with YAML-formatted metadata (front matter) above title', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: { 'no-foo': { meta: { docs: { description: 'Description.' }, }, create(context) {} }, },
configs: { recommended: { rules: { 'test/no-foo': 'error', } } }
};`,

'README.md': '## Rules\n',

// YAML-formatted metadata (front matter) content above title.
'docs/rules/no-foo.md': outdent`
---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
# Some pre-existing title.
Pre-existing notice about the rule being recommended.
## Rule details
Details.
`,

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(PATH_NODE_MODULES),
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
});

it('updates the documentation', async function () {
await generate('.');

expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
});
});

describe('README missing rule list markers but with rules section', function () {
beforeEach(function () {
mockFs({
Expand Down Expand Up @@ -357,19 +406,8 @@ describe('generate (comment markers)', function () {

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description.' }, },
create(context) {}
},
},
configs: {
recommended: {
rules: {
'test/no-foo': 'error',
}
}
}
rules: { 'no-foo': { meta: { docs: { description: 'Description.' }, }, create(context) {} }, },
configs: { recommended: { rules: { 'test/no-foo': 'error', } } }
};`,

'README.md':
Expand Down Expand Up @@ -398,4 +436,99 @@ describe('generate (comment markers)', function () {
expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
});
});

describe('rule doc with YAML-formatted metadata (front matter) above title and comment marker', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: { 'no-foo': { meta: { docs: { description: 'Description.' }, }, create(context) {} }, },
configs: { recommended: { rules: { 'test/no-foo': 'error', } } }
};`,

'README.md': '## Rules\n',

// YAML-formatted metadata (front matter) above title.
'docs/rules/no-foo.md': outdent`
---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
# Outdated title.
Outdated content.
<!-- end auto-generated rule header -->
## Rule details
Details.
`,

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(PATH_NODE_MODULES),
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
});

it('updates the documentation', async function () {
await generate('.');

expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
});
});

describe('rule doc with YAML-formatted metadata (front matter) and nothing else', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
exports: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: { 'no-foo': { meta: { docs: { description: 'Description.' }, }, create(context) {} }, },
configs: { recommended: { rules: { 'test/no-foo': 'error', } } }
};`,

'README.md': '## Rules\n',

// YAML-formatted metadata (front matter) only.
'docs/rules/no-foo.md': outdent`
---
pageClass: "rule-details"
sidebarDepth: 0
title: "plugin/rule-name"
description: "disallow foo"
since: "v0.12.0"
---
`,

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(PATH_NODE_MODULES),
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
});

it('updates the documentation', async function () {
await generate('.');

expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
});
});
});
7 changes: 1 addition & 6 deletions test/lib/generate/configs-test.ts
Expand Up @@ -407,12 +407,7 @@ describe('generate (configs)', function () {

'index.js': `
export default {
rules: {
'no-foo': {
meta: { docs: { description: 'Description.' }, },
create(context) {}
},
},
rules: { 'no-foo': { meta: { docs: { description: 'Description.' }, }, create(context) {} }, },
configs: {
recommended: {
rules: {
Expand Down

0 comments on commit 08a1ed2

Please sign in to comment.