Closed R-Bower closed 5 months ago
I do think being able to author docs with an MDX type workflow would be pretty cool.
You can do pretty close to this already with Astro and the Analog integration for Angular components.
Were you thinking about something different than this?
I was hoping to stay within the Analog ecosystem and avoid using Astro altogether.
Will be revisiting this soon with some discussion with @nartc
Curious about the status of this! Would love to move the spartan docs to an Mdx approach 😍
I might not have the technical chops to actually pull this off but if you can point me in a direction I'm keen to sink some time into figuring out how to get this done (or at least get some kind of prototype going) @brandonroberts
@joshuamorony Awesome. @nartc can add some perspective here too, but I would love to see Angular components able to be used within MDX files. That involves making a compatibility layer with the JSX runtime, and creating Angular components dynamically.
I have a prototype of this on a branch but it needs more work.
I think previous attempts have used a combination of markdown and Angular elements, but I wouldn't lean towards that solution again.
Interested to hear your thoughts
@brandonroberts would it be ok for me to investigate introducing this functionality not through MDX but through some additional file type (like .ngx
) - I don't know the internals all that well yet but I think that MDsveX doesn't actually support MDX for rendering Svelte components in markdown, they have their own custom parsing that uses .svx
files: https://mdsvex.com/docs
I've still got a lot of research to do around this but my understanding currently is that the process (for MDsveX) is something like:
.svx
files as markdownSo as a first attempt I would be interested into essentially seeing if I can follow this same general process for Angular and whatever custom file extension we would want to use.
Does this sound like it might be viable?
@joshuamorony Sure, its worth a shot. Definitely on board with the .ngx
format. I think the last step in the proposal with rendering a compiled Angular component will be a challenge, but challenges are good 🙂.
Alright I'll get on it and see what happens 🤞
@brandonroberts I've been messing around with this and have an extremely early/unfinished prototype - I have actually managed to get an Angular component rendering in the markdown, but before I go further I wanted to check in to see if:
1) This idea will work with the overall AnalogJS approach/philosophy 2) The idea isn't completely ridiculous - this is definitely out of my comfort zone so I really don't have a good sense of whether this is sensible or not
The basic idea is this. I've been able to add a <script>
section to the markdown files, e.g:
---
title: My Third Post
slug: my-third-post
description: My Third Post Description
coverImage: https://images.unsplash.com/photo-1481627834876-b7833e8f5570?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80
---
<script>
import { TestComponent } from '../app/components/test.component'
</script>
### Section 1
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
I then wrote a Vite plugin that transforms the code and returns it in this format:
const outputCode = `
export const markdown = \`${escapedContent}\`;
export const componentMap = ${JSON.stringify(componentMap)};
`;
Rather than returning the raw markdown, it returns a module containing both the markdown and a componentMap
- this is generated from the <script>
tag and maps the names of the components being used to their paths.
Within the generateBundle
for the plugin I then create a file that exports every component that is being used in any of the markdown files (this of course assumes only standalone components are used). This file also export a map to map the string name of the component to the actual component:
generateBundle() {
const componentFile = `
${Object.entries(ngxComponentData)
.map(([name, path]) => `import { ${name} } from '${path}';`)
.join('\n')}
export const MarkdownComponents = [${Object.keys(
ngxComponentData
).join(', ')}]
export const markdownComponentMap: Record<string, any> = {
${Object.keys(ngxComponentData)
.map((name) => `'${name}': ${name},`)
.join('\n')}
}
`;
const filePath = path.resolve(__dirname, 'src/app/components.ts');
fs.writeFileSync(filePath, componentFile);
},
This results in a file that looks like this:
import { TestComponent } from '../app/components/test.component';
export const MarkdownComponents = [TestComponent];
export const markdownComponentMap: Record<string, any> = {
TestComponent: TestComponent,
};
The rest of this process is perhaps even more hacky/less thought out, but for now just to see if I could actually get something rendering I added the MarkdownComponents
from this file to the imports of the [slug].page.ts
component. I'm also passing in the map of all components, and the specific components used in this blog post to the analog-markdown
component:
<analog-markdown
[content]="post.content"
[componentMap]="post.componentMap"
[components]="markdownComponentMap"
></analog-markdown>
Where I then render each of the supplied components:
for (const componentName of Object.keys(this.componentMap)) {
const componentToRender = this.components[componentName];
const componentRef =
this.viewContainer.createComponent(componentToRender);
this.viewContainer.insert(componentRef.hostView);
}
For now this just renders them after the content - I will still need to get them rendering in the correct place.
This is all very rough and hacky obviously and a lot more thought would need to go into it, but I mostly just want a sanity check right now to see if this path is worth pursuing further or not.
@joshuamorony I think this approach is similar to one I tried also, and might work. I do have a few questions on pursuing it further, mostly because of the way Angular works today.
generateBundle
happens during the build, right? We would have to have the file generated earlier so that file/import would be available during development also.I think it ultimately depends on if we want the markdown authoring to be the sole source of the document and we use some sort of transform to turn that into a component that can be compiled. I have a couple of ideas, but need to dig into them more.
@brandonroberts
1) Yes - right now I am just creating that file manually to begin with but I suppose we have a few options here e.g a) it's something manually created when integrating this feature b) this file is just something that lives in all Analog projects c) maybe we use a schematic when integrating this functionality. Not sure what the best approach would be, and I suppose it will depend on how tightly integrated this feature is. At the moment I've actually just gone with .md files (rather than having separate ngx files) and it's all just part of the normal markdown handling - perhaps this is just something Analog could support out of the box with .md files, because it doesn't effect the normal parsing of markdown if components aren't being used (at least not at the moment)
2) This is another thing I need to put more thought into but what I have been planning on so far is using the "forbidden" syntax, e.g:
---
title: My Third Post
slug: my-third-post
description: My Third Post Description
coverImage: https://images.unsplash.com/photo-1481627834876-b7833e8f5570?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80
---
<script>
import { TestComponent } from '../app/components/test.component'
</script>
### Section 1
<TestComponent />
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
This removes any need to deal with selectors - I already have the name/path parsed in the script section, then when figuring out where to inject the components within the markdown content I can (theoretically) just use the component map to match up the name to where it needs to be added (i.e. I could just do a regex match for <TestComponent />
and then use that TestComponent
name which is in the component map to create/add the component in the correct place.
I haven't actually given thought to how I would handle selectors if we want to go that way, but this seems harder since there is no direct link between the name from the import and the selector used in the markdown.
Given the work we've done with .analog files, this is now possible https://github.com/analogjs/analog/pull/879
Which scope/s are relevant/related to the feature request?
content
Information
I've been using ng-doc lately as a Storybook alternative for my component library. They have a nifty feature that allows you to use Angular components in markdown. This brings me closer to the ideal React + MDX authoring experience that I want for my documentation site, but there are still some limitations (namely, speed).
I'd like to be able to render Angular components from Markdown. Is this a possibility today? If not, is it something that can be done in the future?
Describe any alternatives/workarounds you're currently using
None
I would be willing to submit a PR to fix this issue