With ActiveMDX, you can extract meaningful information from the content of your writing. Structured writing can become structured data. Combine this with the power of React and MDX, and ActiveMDX can be used to build applications that are powered by your writing.
MDX gives us the ability to display markdown writing with React components.
Traditional markdown renders static HTML. MDX renders as React components and gives you the ability to control how each standard markdown html element gets rendered. By itself, this is a very powerful way to display your writing and gives you a lot of creative power. People have used it to build interactive code demos which you can live edit and see the results rendered in real time, and much more.
ActiveMDX takes things a step further by giving you the ability to tap into the AST of your markdown writing and extract elements of your writing as data. This gives you the ability to present your writing in different ways, as well as the ability to automate different tasks using your writing by converting it into JSON objects.
ActiveMDX provides a Collection class that turns a folder of MDX documents into a queryable database. Each different type of document is a Model, or a table in a database. Each document of those types are the rows in that database.
import { Collection } from "@active-mdx/core"
import Epic from "./models/Epic"
import Story from "./models/Story"
const collection = new Collection({
rootPath: "./docs"
})
await collection.load()
const completedStories = await Story.query((qb) =>
qb.where("status", "completed")
).fetchAll()
// register a common query
Epic.query("completedStories", (qb) => qb.where("status", "completed"))
// run the common query
Epic.query("completedStories")
An ActiveMDX Model takes a document which conforms to a common structure, and turns it into a JavaScript object that can be used in any way you can imagine.
import { Model } from "@active-mdx/core"
export default class Story extends Model {
// This lets you validate / lint your documents to make sure they
// have the data you expect
static get schema() {
const { joi } = this
return joi.object({
meta: joi.object({
status: joi
.string()
.required()
.allow("created", "todo", "in-progress", "qa", "completed")
})
})
}
// You can relate to other model instances based on what is in this document's content
epic() {
return this.belongsTo("Epic")
}
// When you create new documents, we can pre-fill the metadata
get defaults() {
return {
meta: {
status: "created",
estimates: {
low: 0,
high: 0
}
}
}
}
get isComplete() {
return this.meta.status === "complete"
}
// Get all of the links from the Mockups section of the document
get mockupLinks() {
const { toString } = this.document.utils
return Object.fromEntries(
this.document
.querySection("Mockups")
.selectAll("link")
.map((link) => [toString(link), link.url])
)
}
// Get a list of acceptance criteria items from the Acceptance Criteria section of the document
get acceptanceCriteria() {
const { toString } = this.document.utils
return this.document
.querySection("Acceptance Criteria")
.selectAll("listItem")
.map(toString)
}
}
With the action system, you can run functions using your collection and different models. In the example I've written about here, you could do things like publish all the stories to Github or Jira using their API. To do that we just define the action publish-to-github
Story.action("publish-to-github", async function (story) {
await githubAPI.issues.create({
title,
body: story.document.content,
labels: story.meta.labels
})
})
We can then use the AMDX CLI to run that action
$ amdx action publish-to-github stories/authentication/a-user-should-be-able-to-register
With ActiveMDX, you can extract meaningful information from the content of your writing. Structured writing can become structured data. Combine this with the power of React and MDX, and ActiveMDX can be used to build applications that are powered by your writing.
MDX gives us the ability to display markdown writing with React components.
Traditional markdown renders static HTML. MDX renders as React components and gives you the ability to control how each standard markdown html element gets rendered. By itself, this is a very powerful way to display your writing and gives you a lot of creative power. People have used it to build interactive code demos which you can live edit and see the results rendered in real time, and much more.
ActiveMDX takes things a step further by giving you the ability to tap into the AST of your markdown writing and extract elements of your writing as data. This gives you the ability to present your writing in different ways, as well as the ability to automate different tasks using your writing by converting it into JSON objects.
ActiveMDX provides a Collection class that turns a folder of MDX documents into a queryable database. Each different type of document is a Model, or a table in a database. Each document of those types are the rows in that database.
import { Collection } from "@active-mdx/core"
import Epic from "./models/Epic"
import Story from "./models/Story"
const collection = new Collection({
rootPath: "./docs"
})
await collection.load()
const completedStories = await Story.query((qb) =>
qb.where("status", "completed")
).fetchAll()
// register a common query
Epic.query("completedStories", (qb) => qb.where("status", "completed"))
// run the common query
Epic.query("completedStories")
An ActiveMDX Model takes a document which conforms to a common structure, and turns it into a JavaScript object that can be used in any way you can imagine.
import { Model } from "@active-mdx/core"
export default class Story extends Model {
// This lets you validate / lint your documents to make sure they
// have the data you expect
static get schema() {
const { joi } = this
return joi.object({
meta: joi.object({
status: joi
.string()
.required()
.allow("created", "todo", "in-progress", "qa", "completed")
})
})
}
// You can relate to other model instances based on what is in this document's content
epic() {
return this.belongsTo("Epic")
}
// When you create new documents, we can pre-fill the metadata
get defaults() {
return {
meta: {
status: "created",
estimates: {
low: 0,
high: 0
}
}
}
}
get isComplete() {
return this.meta.status === "complete"
}
// Get all of the links from the Mockups section of the document
get mockupLinks() {
const { toString } = this.document.utils
return Object.fromEntries(
this.document
.querySection("Mockups")
.selectAll("link")
.map((link) => [toString(link), link.url])
)
}
// Get a list of acceptance criteria items from the Acceptance Criteria section of the document
get acceptanceCriteria() {
const { toString } = this.document.utils
return this.document
.querySection("Acceptance Criteria")
.selectAll("listItem")
.map(toString)
}
}
With the action system, you can run functions using your collection and different models. In the example I've written about here, you could do things like publish all the stories to Github or Jira using their API. To do that we just define the action publish-to-github
Story.action("publish-to-github", async function (story) {
await githubAPI.issues.create({
title,
body: story.document.content,
labels: story.meta.labels
})
})
We can then use the AMDX CLI to run that action
$ amdx action publish-to-github stories/authentication/a-user-should-be-able-to-register