Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(docs): respect custom README content when writing to a custom path #5648

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/docs/generate-doc-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined> => {
export const getUserReadmeContent = async (
compilerCtx: d.CompilerCtx,
readmePath: string,
): Promise<string | undefined> => {
try {
const existingContent = await compilerCtx.fs.readFile(readmePath);
// subtract one to get everything up to, but not including the auto generated comment
Expand Down
48 changes: 41 additions & 7 deletions src/compiler/docs/readme/output-docs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { join, relative } from '@utils';
import { join, normalizePath, 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';
Expand All @@ -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,
Expand All @@ -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 =
normalizePath(readmeOutput.dir) !== normalizePath(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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would determining the README path at

const readmePath = normalizePath(join(dirPath, 'readme.md'));
make it such that we don't need to read the README file from disk again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can't really decide a 'canonical' README path at that point, because we're not operating on docs-readme output targets at that point

Or in other words, if I set a custom readme output dir on the docs-readme OT I don't think that should affect how the docs-json OT is handled

if we want that behavior then the readme location configuration should not be output target specific, but something in e.g. the @Component decorator

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can't really decide a 'canonical' README path at that point, because we're not operating on docs-readme output targets at that point

But hasn't a user specified the canonical README at that point (and we've defaulted to the directory of the component if they haven't)? If the README.md is the source of truth, doesn't that always affect the docs-json OT, even today?

Maybe the question here is, how many README files should exist, and which ones influence 'docs-json'?

The Stencil component starter gives us a README by default for my-component, but on main today, that doesn't get regenerated/updated if we specify dir on the 'docs-readme' OT. With that in mind, I'd expect the README in the custom dir to be the be the 'canonical' one (and where we pull README data from for 'docs-json').

For example:

  1. npm init stencil@latest component readme-out-test && cd $_ && npm i to generate a component starter
  2. Modifying stencil.config.ts before running a build:
diff --git a/stencil.config.ts b/stencil.config.ts
index 59cd15a..8167fb3 100644
--- a/stencil.config.ts
+++ b/stencil.config.ts
@@ -14,6 +14,7 @@ export const config: Config = {
     },
     {
       type: 'docs-readme',
+      dir: 'my-custom-dir'
     },
     {
       type: 'www',
  1. Deleting src/components/my-component/readme.md that was a part of the starter
  2. Running a build with npm run build

Would all result in the following dir tree:

my-custom-dir
└── components
    └── my-component
        └── readme.md
src
├── components
│   └── my-component
│       ├── my-component.css
│       ├── my-component.e2e.ts
│       ├── my-component.spec.ts
│       └── my-component.tsx
├── components.d.ts
├── index.html
├── index.ts
└── utils
    ├── utils.spec.ts
    └── utils.ts

Where README.md doesn't exist in src, because we don't generate it

Does that make sense? I've been staring at Jira for far too long today, and my brain feels a little static-y 😆

As an aside, the behavior today on main and this branch creates a bit of a conundrum - we've left a README file next to the component in the filesystem, with no real way to clean it up ourselves (users will just have to do that). We have the same behavior on this branch, but I think that's out of scope here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the problem is that the way this is built we just don't have a 'canonical' README location or a way to specify that at present (or, we don't have one without making some assumptions that wouldn't be warranted in all cases). In particular, nothing prevents me from adding multiple different docs-readme output targets, each with different dir properties. Stencil will handle them all, and with this change for each one it will 'respect' any custom content that the user has in each readme - this is in line with generally how our output target processing is based on "give me all of the dist OTs and I'll process them," but it does mean that I don't think we can define a canonical readme location right now based on how this thing works without making some assumption about what the user 'means'.

Right now in the docs-json output target (i.e. the code in src/compiler/docs/generate-doc-data.ts there) we have an assumption that the readme next to the component is the 'canonical' one. We could add a check where we make some assumptions about the user wants, and do something like

  1. is there a single docs-readme OT on the config?
  2. does that have the dir target?

if yes on both counts, then use the readme at that path as the readme for the docs-json OT (and just in general). We could do that, but we'd then need to look at maybe adding validation that you only specify one docs-readme OT, or designate a 'primary' one, or just use the first one silently, etc.

Thinking through some of the additional complexity here makes me hesitant to go forward with a change like this right now, I think especially it's out of scope for this PR which is just patching the existing functionality for the docs-readme OT itself. I will add a ticket to the backlog to consider whether we should approach this differently in the future / add a concept of a 'canonical' readme that's 1:1 associated with a file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In particular, nothing prevents me from adding multiple different docs-readme output targets, each with different dir properties.

Ahhh that's what I was missing - I was only thinking about there being 0-1, and multiple READMEs scattered around the project directory as a result of multiple runs of stencil build --docs (and the like) with difference configurations.

I agree with your assessment there - thanks for the explanation!

: 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}`);
Expand All @@ -42,15 +70,15 @@ export const generateReadme = async (
};

export const generateMarkdown = (
userContent: string,
userContent: string | undefined,
cmp: d.JsonDocsComponent,
cmps: d.JsonDocsComponent[],
readmeOutput: d.OutputTargetDocsReadme,
) => {
//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,
'',
'',
Expand Down Expand Up @@ -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');
};
5 changes: 5 additions & 0 deletions src/declarations/stencil-public-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# styleurls-component

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.

<!-- Auto Generated Below -->


## CSS Custom Properties

| Name | Description |
| ------- | ------------ |
| `--one` | Property One |
| `--two` | Property Two |


----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
4 changes: 4 additions & 0 deletions test/docs-readme/stencil.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ export const config: Config = {
{
type: 'dist',
},
{
type: 'docs-readme',
dir: 'custom-readme-output',
},
],
};