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

Allow AlpineJS syntax extensions in Markdown #3554

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 5 additions & 0 deletions .changeset/swift-rocks-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/markdown-remark': patch
---

Allow AlpineJS syntax extensions in Markdown
10 changes: 8 additions & 2 deletions packages/markdown/remark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@
"test": "mocha --exit --timeout 20000"
},
"dependencies": {
"@astrojs/micromark-extension-mdx-jsx": "^1.0.3",
"@astrojs/prism": "^0.4.1",
"acorn": "^8.7.1",
"acorn-jsx": "^5.3.2",
"assert": "^2.0.0",
"github-slugger": "^1.4.0",
"mdast-util-mdx-expression": "^1.2.0",
"mdast-util-mdx-jsx": "^1.2.0",
"mdast-util-to-string": "^3.1.0",
"micromark-extension-mdx-jsx": "^1.0.3",
"micromark-extension-mdxjs": "^1.0.0",
"micromark-extension-mdx-expression": "^1.0.3",
"micromark-extension-mdx-md": "^1.0.0",
"micromark-extension-mdxjs-esm": "^1.0.3",
"micromark-util-combine-extensions": "^1.0.0",
"prismjs": "^1.28.0",
"rehype-raw": "^6.1.1",
"rehype-stringify": "^9.0.3",
Expand All @@ -55,6 +60,7 @@
"@types/unist": "^2.0.6",
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
"micromark-util-types": "^1.0.2",
"mocha": "^9.2.2"
}
}
32 changes: 32 additions & 0 deletions packages/markdown/remark/src/mdxjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Note: The code in this file is based on `micromark-extension-mdxjs`
// and was adapted to use our fork `@astrojs/micromark-extension-mdx-jsx`
// instead of `micromark-extension-mdx-jsx` to allow some extended syntax.
// See `@astrojs/micromark-extension-mdx-jsx` on NPM for more details.

import { Parser } from 'acorn';
import acornJsx from 'acorn-jsx';
import { combineExtensions } from 'micromark-util-combine-extensions';
import { mdxExpression } from 'micromark-extension-mdx-expression';
import { mdxJsx } from '@astrojs/micromark-extension-mdx-jsx';
import { mdxMd } from 'micromark-extension-mdx-md';
import { mdxjsEsm } from 'micromark-extension-mdxjs-esm';
import type { Options } from 'micromark-extension-mdx-expression';
import type { Extension } from 'micromark-util-types';

export function mdxjs(options: Options): Extension {
const settings: any = Object.assign(
{
acorn: Parser.extend(acornJsx()),
acornOptions: { ecmaVersion: 2020, sourceType: 'module' },
addResult: true
},
options
);

return combineExtensions([
mdxjsEsm(settings),
mdxExpression(settings),
mdxJsx(settings),
mdxMd
]);
}
2 changes: 1 addition & 1 deletion packages/markdown/remark/src/remark-mdxish.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as fromMarkdown from 'mdast-util-from-markdown';
import type { Tag } from 'mdast-util-mdx-jsx';
import { mdxjs } from 'micromark-extension-mdxjs';
import { mdxjs } from './mdxjs.js';
import { mdxFromMarkdown, mdxToMarkdown } from './mdast-util-mdxish.js';

export default function remarkMdxish(this: any, options = {}) {
Expand Down
122 changes: 122 additions & 0 deletions packages/markdown/remark/test/strictness.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,126 @@ describe('strictness', () => {
`<img src="hi.jpg" /></p>`
);
});

it('should allow attribute names starting with ":" after element names', async () => {
const { code } = await renderMarkdown(
`<div :class="open ? '' : 'hidden'">Test</div>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<div :class="open ? '' : 'hidden'">Test</div>`);
});

it('should allow attribute names starting with ":" after local element names', async () => {
const { code } = await renderMarkdown(
`<div.abc :class="open ? '' : 'hidden'">x</div.abc>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<div.abc :class="open ? '' : 'hidden'">x</div.abc>`);
});

it('should allow attribute names starting with ":" after attribute names', async () => {
const { code } = await renderMarkdown(
`<input type="text" disabled :placeholder="hi">`,
{}
);

chai
.expect(code.trim())
.to.equal(`<input type="text" disabled :placeholder="hi" />`);
});

it('should allow attribute names starting with ":" after local attribute names', async () => {
const { code } = await renderMarkdown(
`<input type="text" x-test:disabled :placeholder="hi">`,
{}
);

chai
.expect(code.trim())
.to.equal(`<input type="text" x-test:disabled :placeholder="hi" />`);
});

it('should allow attribute names starting with ":" after attribute values', async () => {
const { code } = await renderMarkdown(
`<input type="text" :placeholder="placeholder">`,
{}
);

chai
.expect(code.trim())
.to.equal(`<input type="text" :placeholder="placeholder" />`);
});

it('should allow attribute names starting with "@" after element names', async () => {
const { code } = await renderMarkdown(
`<button @click="handleClick">Test</button>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<button @click="handleClick">Test</button>`);
});

it('should allow attribute names starting with "@" after local element names', async () => {
const { code } = await renderMarkdown(
`<button.local @click="handleClick">Test</button.local>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<button.local @click="handleClick">Test</button.local>`);
});

it('should allow attribute names starting with "@" after attribute names', async () => {
const { code } = await renderMarkdown(
`<button disabled @click="handleClick">Test</button>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<button disabled @click="handleClick">Test</button>`);
});

it('should allow attribute names starting with "@" after local attribute names', async () => {
const { code } = await renderMarkdown(
`<button x-test:disabled @click="handleClick">Test</button>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<button x-test:disabled @click="handleClick">Test</button>`);
});

it('should allow attribute names starting with "@" after attribute values', async () => {
const { code } = await renderMarkdown(
`<button type="submit" @click="handleClick">Test</button>`,
{}
);

chai
.expect(code.trim())
.to.equal(`<button type="submit" @click="handleClick">Test</button>`);
});

it('should allow attribute names containing dots', async () => {
const { code } = await renderMarkdown(
`<input x-on:input.debounce.500ms="fetchResults">`,
{}
);

chai
.expect(code.trim())
.to.equal(`<input x-on:input.debounce.500ms="fetchResults" />`);
});

});
Loading