From b569ea7a9ad5d3b43eba3cf5c556cfab2c5e9e5f Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Wed, 10 Apr 2024 15:02:41 -0400 Subject: [PATCH] fix(docs): respect custom README content when writing to a custom path On the `docs-readme` output target it's possible to set a custom output location with the `.dir` property and the README files generation for components will then be output to relative paths (like `my-component/readme.md`) within that directory. This fixes a bug where that behavior didn't properly respect any manually-entered content in those readme files, so that if, for instance, you set the output to `custom-readme-output` and had `"My Custom Text"` at the top of `custom-readme-output/components/my-component/readme.md` then running a build would overwrite your custom text. This changes things so that we read the content of the custom readme and use that as the basis for the new text that we're going to write to disk. This has the effect of preserving the custom text that a user might have input. fixes #5400 --- src/compiler/docs/generate-doc-data.ts | 5 +- src/compiler/docs/readme/output-docs.ts | 46 ++++++++++++++++--- src/declarations/stencil-public-compiler.ts | 5 ++ .../components/my-component/readme.md | 31 +++++++++++++ test/docs-json/stencil.config.ts | 4 ++ 5 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 test/docs-json/custom-readme-output/components/my-component/readme.md diff --git a/src/compiler/docs/generate-doc-data.ts b/src/compiler/docs/generate-doc-data.ts index 7788e5fd582f..8246b809f138 100644 --- a/src/compiler/docs/generate-doc-data.ts +++ b/src/compiler/docs/generate-doc-data.ts @@ -377,7 +377,10 @@ export const getNameText = (name: string, tags: d.JsonDocsTag[]) => { * @returns the user generated content that occurs before {@link AUTO_GENERATE_COMMENT}. If no user generated content * exists, or if there was an issue reading the file, return `undefined` */ -const getUserReadmeContent = async (compilerCtx: d.CompilerCtx, readmePath: string): Promise => { +export const getUserReadmeContent = async ( + compilerCtx: d.CompilerCtx, + readmePath: string, +): Promise => { try { const existingContent = await compilerCtx.fs.readFile(readmePath); // subtract one to get everything up to, but not including the auto generated comment diff --git a/src/compiler/docs/readme/output-docs.ts b/src/compiler/docs/readme/output-docs.ts index 7b5962779efa..8d7146270d40 100644 --- a/src/compiler/docs/readme/output-docs.ts +++ b/src/compiler/docs/readme/output-docs.ts @@ -2,6 +2,7 @@ import { join, relative } from '@utils'; import type * as d from '../../../declarations'; import { AUTO_GENERATE_COMMENT } from '../constants'; +import { getUserReadmeContent } from '../generate-doc-data'; import { stylesToMarkdown } from './markdown-css-props'; import { depsToMarkdown } from './markdown-dependencies'; import { eventsToMarkdown } from './markdown-events'; @@ -12,6 +13,23 @@ import { propsToMarkdown } from './markdown-props'; import { slotsToMarkdown } from './markdown-slots'; import { usageToMarkdown } from './markdown-usage'; +/** + * Generate a README for a given component and write it to disk. + * + * Typically the README is going to be a 'sibling' to the component's source + * code (i.e. written to the same directory) but the user may also configure a + * custom output directory by setting {@link d.OutputTargetDocsReadme.dir}. + * + * Output readme files also include {@link AUTO_GENERATE_COMMENT}, and any + * text located _above_ that comment is preserved when the new readme is written + * to disk. + * + * @param config a validated Stencil config + * @param compilerCtx the current compiler context + * @param readmeOutputs docs-readme output targets + * @param docsData documentation data for the component of interest + * @param cmps metadata for all the components in the project + */ export const generateReadme = async ( config: d.ValidatedConfig, compilerCtx: d.CompilerCtx, @@ -25,10 +43,20 @@ export const generateReadme = async ( await Promise.all( readmeOutputs.map(async (readmeOutput) => { if (readmeOutput.dir) { - const readmeContent = generateMarkdown(userContent, docsData, cmps, readmeOutput); - const relPath = relative(config.srcDir, docsData.readmePath); - const absPath = join(readmeOutput.dir, relPath); - const results = await compilerCtx.fs.writeFile(absPath, readmeContent); + const relativeReadmePath = relative(config.srcDir, docsData.readmePath); + const readmeOutputPath = join(readmeOutput.dir, relativeReadmePath); + + const currentReadmeContent = + readmeOutput.dir !== config.srcDir + ? // The user set a custom `.dir` property, which is where we're going + // to write the updated README. We need to read the non-automatically + // generated content from that file and preserve that. + await getUserReadmeContent(compilerCtx, readmeOutputPath) + : userContent; + + const readmeContent = generateMarkdown(currentReadmeContent, docsData, cmps, readmeOutput); + + const results = await compilerCtx.fs.writeFile(readmeOutputPath, readmeContent); if (results.changedContent) { if (isUpdate) { config.logger.info(`updated readme docs: ${docsData.tag}`); @@ -42,7 +70,7 @@ export const generateReadme = async ( }; export const generateMarkdown = ( - userContent: string, + userContent: string | undefined, cmp: d.JsonDocsComponent, cmps: d.JsonDocsComponent[], readmeOutput: d.OutputTargetDocsReadme, @@ -50,7 +78,7 @@ export const generateMarkdown = ( //If the readmeOutput.dependencies is true or undefined the dependencies will be generated. const dependencies = readmeOutput.dependencies !== false ? depsToMarkdown(cmp, cmps) : []; return [ - userContent, + userContent || '', AUTO_GENERATE_COMMENT, '', '', @@ -78,6 +106,12 @@ const getDocsDeprecation = (cmp: d.JsonDocsComponent) => { return []; }; +/** + * Get a minimal default README for a Stencil component + * + * @param docsData documentation data for the component of interest + * @returns a minimal README template for that component + */ const getDefaultReadme = (docsData: d.JsonDocsComponent) => { return [`# ${docsData.tag}`, '', '', ''].join('\n'); }; diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts index 1834c6faa5b1..7d1c3d0cd639 100644 --- a/src/declarations/stencil-public-compiler.ts +++ b/src/declarations/stencil-public-compiler.ts @@ -2266,6 +2266,11 @@ export interface OutputTargetDocsVscode extends OutputTargetBase { export interface OutputTargetDocsReadme extends OutputTargetBase { type: 'docs-readme'; + /** + * The root directory where README files should be written + * + * defaults to {@link Config.srcDir} + */ dir?: string; dependencies?: boolean; footer?: string; diff --git a/test/docs-json/custom-readme-output/components/my-component/readme.md b/test/docs-json/custom-readme-output/components/my-component/readme.md new file mode 100644 index 000000000000..98f5b774ffda --- /dev/null +++ b/test/docs-json/custom-readme-output/components/my-component/readme.md @@ -0,0 +1,31 @@ +This file is in a custom location, set with `.dir` on the `docs-readme` OT. + +The content here above the 'auto-generation' comment shouldn't be overwritten. + +This is a regression test for the issue reported in ionic-team/stencil#5400. + + + + +## Methods + +### `onDidDismiss(arg: T) => Promise>` + +A comment, which should be included, I should think! + +#### Parameters + +| Name | Type | Description | +| ----- | ---- | ----------- | +| `arg` | `T` | | + +#### Returns + +Type: `Promise>` + + + + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/test/docs-json/stencil.config.ts b/test/docs-json/stencil.config.ts index 33972dc86efc..bd5f99bfd44c 100644 --- a/test/docs-json/stencil.config.ts +++ b/test/docs-json/stencil.config.ts @@ -8,5 +8,9 @@ export const config: Config = { file: 'docs.json', supplementalPublicTypes: 'src/components/interfaces.ts', }, + { + type: 'docs-readme', + dir: 'custom-readme-output', + }, ], };