fix: bundle client-side code for components used in .md pages (#78)

This commit is contained in:
Matt Mulder 2021-04-13 13:04:01 -04:00 committed by GitHub
parent b588581396
commit ac22d94e11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 26 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ lib/
dist/ dist/
*.tsbuildinfo *.tsbuildinfo
.DS_Store .DS_Store
test/fixtures/*/_site/

View file

@ -8,6 +8,7 @@ import esbuild from 'esbuild';
import { promises as fsPromises } from 'fs'; import { promises as fsPromises } from 'fs';
import { parse } from '../parser/index.js'; import { parse } from '../parser/index.js';
import { transform } from '../compiler/transform/index.js'; import { transform } from '../compiler/transform/index.js';
import { convertMdToAstroSource } from '../compiler/index.js';
import { getAttrValue } from '../ast.js'; import { getAttrValue } from '../ast.js';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import babelParser from '@babel/parser'; import babelParser from '@babel/parser';
@ -72,12 +73,17 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin
const imports = new Set<string>(); const imports = new Set<string>();
// Only astro files // Only astro files
if (!filename.pathname.endsWith('astro')) { if (!filename.pathname.endsWith('.astro') && !filename.pathname.endsWith('.md')) {
return imports; return imports;
} }
const extensions = astroConfig.extensions || defaultExtensions; const extensions = astroConfig.extensions || defaultExtensions;
const source = await readFile(filename, 'utf-8');
let source = await readFile(filename, 'utf-8');
if (filename.pathname.endsWith('.md')) {
source = await convertMdToAstroSource(source);
}
const ast = parse(source, { const ast = parse(source, {
filename, filename,
}); });

View file

@ -48,13 +48,9 @@ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): P
} }
/** /**
* .md -> .jsx * .md -> .astro source
* Core function processing Markdown, but along the way also calls convertAstroToJsx().
*/ */
async function convertMdToJsx( export async function convertMdToAstroSource(contents: string): Promise<string> {
contents: string,
{ compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }
): Promise<TransformResult> {
const { data: frontmatterData, content } = matter(contents); const { data: frontmatterData, content } = matter(contents);
const { headers, headersExtension } = createMarkdownHeadersCollector(); const { headers, headersExtension } = createMarkdownHeadersCollector();
const { htmlAstro, mdAstro } = encodeAstroMdx(); const { htmlAstro, mdAstro } = encodeAstroMdx();
@ -80,15 +76,24 @@ async function convertMdToJsx(
// Break it up here so that the HTML parser won't detect it. // Break it up here so that the HTML parser won't detect it.
const stringifiedSetupContext = JSON.stringify(contentData).replace(/\<\/script\>/g, `</scrip" + "t>`); const stringifiedSetupContext = JSON.stringify(contentData).replace(/\<\/script\>/g, `</scrip" + "t>`);
const raw = `--- return `---
${imports} ${imports}
${frontmatterData.layout ? `import {__renderPage as __layout} from '${frontmatterData.layout}';` : 'const __layout = undefined;'} ${frontmatterData.layout ? `import {__renderPage as __layout} from '${frontmatterData.layout}';` : 'const __layout = undefined;'}
export const __content = ${stringifiedSetupContext}; export const __content = ${stringifiedSetupContext};
--- ---
<section>${mdHtml}</section>`; <section>${mdHtml}</section>`;
}
/**
* .md -> .jsx
* Core function processing Markdown, but along the way also calls convertAstroToJsx().
*/
async function convertMdToJsx(
contents: string,
{ compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }
): Promise<TransformResult> {
const raw = await convertMdToAstroSource(contents);
const convertOptions = { compileOptions, filename, fileID }; const convertOptions = { compileOptions, filename, fileID };
return await convertAstroToJsx(raw, convertOptions); return await convertAstroToJsx(raw, convertOptions);
} }

View file

@ -1,15 +1,22 @@
import { existsSync, promises as fsPromises } from 'fs';
import { join } from 'path';
import { suite } from 'uvu'; import { suite } from 'uvu';
import * as assert from 'uvu/assert'; import * as assert from 'uvu/assert';
import { createRuntime } from '../lib/runtime.js'; import { createRuntime } from '../lib/runtime.js';
import { build } from '../lib/build.js';
import { loadConfig } from '../lib/config.js'; import { loadConfig } from '../lib/config.js';
import { doc } from './test-utils.js'; import { doc } from './test-utils.js';
const { rmdir, readFile } = fsPromises;
const Markdown = suite('Astro Markdown'); const Markdown = suite('Astro Markdown');
let runtime, setupError; let runtime, setupError, fixturePath, astroConfig;
Markdown.before(async () => { Markdown.before(async () => {
const astroConfig = await loadConfig(new URL('./fixtures/astro-markdown', import.meta.url).pathname); fixturePath = new URL('./fixtures/astro-markdown', import.meta.url).pathname;
astroConfig = await loadConfig(fixturePath);
const logging = { const logging = {
level: 'error', level: 'error',
@ -26,6 +33,7 @@ Markdown.before(async () => {
Markdown.after(async () => { Markdown.after(async () => {
(await runtime) && runtime.shutdown(); (await runtime) && runtime.shutdown();
rmdir(join(fixturePath, '_site'), { recursive: true });
}); });
Markdown('No errors creating a runtime', () => { Markdown('No errors creating a runtime', () => {
@ -50,4 +58,13 @@ Markdown('Can load more complex jsxy stuff', async () => {
assert.equal($el.text(), 'Hello world'); assert.equal($el.text(), 'Hello world');
}); });
Markdown('Bundles client-side JS for prod', async () => {
await build(astroConfig);
const complexHtml = await readFile(join(fixturePath, './_site/complex/index.html'), 'utf-8');
assert.match(complexHtml, `import("/_astro/components/Counter.js"`);
assert.ok(existsSync(join(fixturePath, `./_site/_astro/components/Counter.js`)), 'Counter.jsx is bundled for prod');
});
Markdown.run(); Markdown.run();

View file

@ -0,0 +1,7 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
export default function () {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

View file

@ -4,8 +4,10 @@ title: My Blog Post
description: This is a post about some stuff. description: This is a post about some stuff.
import: import:
Hello: '../components/Hello.jsx' Hello: '../components/Hello.jsx'
Counter: '../components/Counter.jsx'
--- ---
## Interesting Topic ## Interesting Topic
<Hello name={`world`} /> <Hello name={`world`} />
<Counter:load />

View file

@ -1,14 +0,0 @@
---
export function setup() {
return {props: {}}
}
---
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>