* fix(vscode): Markdown frontmatter should use TSX, not YAML

* test: add test for #153

* chore: bump deps

* chore: update to use @astrojs scope

* fix: Markdown parse error when only child is `{expression}`

* fix: update renderer edge cases

* fix: failing test

* fix: update renderer
This commit is contained in:
Nate Moore 2021-05-28 17:19:40 -05:00 committed by GitHub
parent 630c36f351
commit 3df41d2308
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 156 additions and 64 deletions

View file

@ -6,7 +6,7 @@
Updated the rendering pipeline for `astro` to truly support any framework.
For the vast majority of use cases, `astro` should _just work_ out of the box. Astro now depends on `@astro-renderer/preact`, `@astro-renderer/react`, `@astro-renderer/svelte`, and `@astro-renderer/vue`, rather than these being built into the core library. This opens the door for anyone to contribute additional renderers for Astro to support their favorite framework, as well as the ability for users to control which renderers should be used.
For the vast majority of use cases, `astro` should _just work_ out of the box. Astro now depends on `@astrojs/renderer-preact`, `@astrojs/renderer-react`, `@astrojs/renderer-svelte`, and `@astrojs/renderer-vue`, rather than these being built into the core library. This opens the door for anyone to contribute additional renderers for Astro to support their favorite framework, as well as the ability for users to control which renderers should be used.
**Features**

View file

@ -1,8 +1,8 @@
---
'@astro-renderer/preact': minor
'@astro-renderer/react': minor
'@astro-renderer/svelte': minor
'@astro-renderer/vue': minor
'@astrojs/renderer-preact': minor
'@astrojs/renderer-react': minor
'@astrojs/renderer-svelte': minor
'@astrojs/renderer-vue': minor
---
Initial release

View file

@ -32,6 +32,6 @@ export default {
tailwindConfig: undefined,
},
/** default array of rendering packages inserted into runtime */
renderers: ['@astro-renderer/preact', '@astro-renderer/react', '@astro-renderer/svelte', '@astro-renderer/vue'],
renderers: ['@astrojs/renderer-preact', '@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'],
};
```

View file

@ -32,10 +32,10 @@
"test": "uvu test -i fixtures -i benchmark -i test-utils.js"
},
"dependencies": {
"@astro-renderer/preact": "0.0.1",
"@astro-renderer/react": "0.0.1",
"@astro-renderer/svelte": "0.0.1",
"@astro-renderer/vue": "0.0.1",
"@astrojs/renderer-preact": "0.0.1",
"@astrojs/renderer-react": "0.0.1",
"@astrojs/renderer-svelte": "0.0.1",
"@astrojs/renderer-vue": "0.0.1",
"@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.9",
"@babel/parser": "^7.13.15",
@ -86,7 +86,7 @@
"sass": "^1.32.13",
"shorthash": "^0.0.2",
"slash": "^4.0.0",
"snowpack": "^3.5.1",
"snowpack": "^3.5.2",
"source-map-support": "^0.5.19",
"string-width": "^5.0.0",
"tiny-glob": "^0.2.8",

View file

@ -492,6 +492,10 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
break;
}
case 'MustacheTag':
if (state.markers.insideMarkdown) {
if (curr === 'out') curr = 'markdown';
}
return;
case 'Comment':
return;
case 'Fragment':
@ -623,7 +627,6 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
case 'Fragment':
case 'Expression':
case 'MustacheTag':
return;
case 'CodeSpan':
case 'CodeFence':
return;

View file

@ -98,7 +98,7 @@ export function scopeRule(selector: string, className: string) {
/** PostCSS Scope plugin */
export default function astroScopedStyles(options: AstroScopedOptions): Plugin {
return {
postcssPlugin: '@astro/postcss-scoped-styles',
postcssPlugin: '@astrojs/postcss-scoped-styles',
Rule(rule) {
rule.selector = scopeRule(rule.selector, options.className);
},

View file

@ -27,13 +27,16 @@ __renderers = [astro, ...__renderers];
const rendererCache = new WeakMap();
/** For a given component, resolve the renderer. Results are cached if this instance is encountered again */
function resolveRenderer(Component: any, props: any = {}) {
async function resolveRenderer(Component: any, props: any = {}, children?: string) {
if (rendererCache.has(Component)) {
return rendererCache.get(Component);
}
for (const __renderer of __renderers) {
const shouldUse = __renderer.check(Component, props);
// Yes, we do want to `await` inside of this loop!
// __renderer.check can't be run in parallel, it
// returns the first match and skips any subsequent checks
const shouldUse = await __renderer.check(Component, props, children);
if (shouldUse) {
rendererCache.set(Component, __renderer);
return __renderer;
@ -67,21 +70,21 @@ export const __astro_component = (Component: any, componentProps: AstroComponent
if (Component == null) {
throw new Error(`Unable to render <${componentProps.displayName}> because it is ${Component}!\nDid you forget to import the component or is it possible there is a typo?`);
}
// First attempt at resolving a renderer (we don't have the props yet, so it might fail if they are required)
let renderer = resolveRenderer(Component);
return async (props: any, ..._children: string[]) => {
if (!renderer) {
// Second attempt at resolving a renderer (this time we have props!)
renderer = resolveRenderer(Component, props);
const children = _children.join('\n');
let renderer = await resolveRenderer(Component, props, children);
if (!renderer) {
// If the user only specifies a single renderer, but the check failed
// for some reason... just default to their preferred renderer.
renderer = (__rendererSources.length === 2) ? __renderers[1] : null;
// Okay now we definitely can't resolve a renderer, so let's throw
if (!renderer) {
const name = typeof Component === 'function' ? Component.displayName ?? Component.name : `{ ${Object.keys(Component).join(', ')} }`;
throw new Error(`No renderer found for ${name}! Did you forget to add a renderer to your Astro config?`);
}
}
const children = _children.join('\n');
const { html } = await renderer.renderToStaticMarkup(Component, props, children);
// If we're NOT hydrating this component, just return the HTML
if (!componentProps.hydrate) {

View file

@ -268,7 +268,7 @@ interface CreateSnowpackOptions {
}
const DEFAULT_HMR_PORT = 12321;
const DEFAULT_RENDERERS = ['@astro-renderer/vue', '@astro-renderer/svelte', '@astro-renderer/react', '@astro-renderer/preact'];
const DEFAULT_RENDERERS = ['@astrojs/renderer-vue', '@astrojs/renderer-svelte', '@astrojs/renderer-react', '@astrojs/renderer-preact'];
/** Create a new Snowpack instance to power Astro */
async function createSnowpack(astroConfig: AstroConfig, options: CreateSnowpackOptions) {

View file

@ -0,0 +1,30 @@
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { doc } from './test-utils.js';
import { setup, setupBuild } from './helpers.js';
const Components = suite('Components tests');
setup(Components, './fixtures/astro-components');
setupBuild(Components, './fixtures/astro-components');
Components('Astro components are able to render framework components', async ({ runtime }) => {
let result = await runtime.load('/');
if (result.error) throw new Error(result);
const $ = doc(result.contents);
const $astro = $('#astro');
assert.equal($astro.children().length, 3, 'Renders astro component');
const $react = $('#react');
assert.not.type($react, 'undefined', 'Renders React component');
const $vue = $('#vue');
assert.not.type($vue, 'undefined', 'Renders Vue component');
const $svelte = $('#svelte');
assert.not.type($svelte, 'undefined', 'Renders Svelte component');
});
Components.run();

View file

@ -15,7 +15,7 @@ DynamicComponents('Loads client-only packages', async ({ runtime }) => {
const exp = /import\("(.+?)"\)/g;
let match, reactRenderer;
while ((match = exp.exec(result.contents))) {
if (match[1].includes('renderers/react/client.js')) {
if (match[1].includes('renderers/renderer-react/client.js')) {
reactRenderer = match[1];
}
}

View file

@ -1,7 +1,7 @@
export default {
renderers: [
'@astro-renderer/preact',
'@astro-renderer/vue',
'@astro-renderer/svelte',
'@astrojs/renderer-preact',
'@astrojs/renderer-vue',
'@astrojs/renderer-svelte',
],
};

View file

@ -0,0 +1,3 @@
{
"workspaceRoot": "../../../../../"
}

View file

@ -0,0 +1,11 @@
---
import ReactComponent from './Component.jsx';
import VueComponent from './Component.vue';
import SvelteComponent from './Component.svelte';
---
<div id="astro">
<ReactComponent />
<VueComponent />
<SvelteComponent />
</div>

View file

@ -0,0 +1,5 @@
import { h } from 'preact';
export default function PreactComponent({ children }) {
return <div id="preact">{children}</div>;
}

View file

@ -0,0 +1,3 @@
<div id="svelte">
<slot />
</div>

View file

@ -0,0 +1,9 @@
<template>
<div id="vue">
<slot />
</div>
</template>
<script>
export default {}
</script>

View file

@ -0,0 +1,9 @@
---
import AstroComponent from '../components/Component.astro';
---
<html>
<head><title>Components</title></head>
<body>
<AstroComponent />
</body>
</html>

View file

@ -1,5 +1,5 @@
export default {
name: '@astro-renderer/react',
name: '@astrojs/renderer-preact',
client: './client',
server: './server',
};

View file

@ -1,5 +1,5 @@
{
"name": "@astro-renderer/preact",
"name": "@astrojs/renderer-preact",
"version": "0.0.1",
"type": "module",
"exports": {

View file

@ -2,15 +2,16 @@ import { h } from 'preact';
import { renderToString } from 'preact-render-to-string';
import StaticHtml from './static-html.js';
function check(Component, props) {
function check(Component, props, children) {
try {
return Boolean(renderToString(h(Component, props)));
const { html } = renderToStaticMarkup(Component, props, children)
return Boolean(html)
} catch (e) {}
return false;
}
function renderToStaticMarkup(Component, props, children) {
const html = renderToString(h(Component, props, h(StaticHtml, { value: children })));
const html = renderToString(h(Component, { ...props, children: h(StaticHtml, { value: children }), innerHTML: children }));
return { html };
}

View file

@ -1,5 +1,5 @@
export default {
name: '@astro-renderer/preact',
name: '@astrojs/renderer-react',
client: './client',
server: './server',
};

View file

@ -1,5 +1,5 @@
{
"name": "@astro-renderer/react",
"name": "@astrojs/renderer-react",
"version": "0.0.1",
"type": "module",
"exports": {

View file

@ -2,15 +2,16 @@ import { createElement as h } from 'react';
import { renderToStaticMarkup as renderToString } from 'react-dom/server.js';
import StaticHtml from './static-html.js';
function check(Component, props) {
function check(Component, props, children) {
try {
return Boolean(renderToString(h(Component, props)));
const { html } = renderToStaticMarkup(Component, props, children)
return Boolean(html)
} catch (e) {}
return false;
}
function renderToStaticMarkup(Component, props, children) {
const html = renderToString(h(Component, props, h(StaticHtml, { value: children })));
const html = renderToString(h(Component, { ...props, children: h(StaticHtml, { value: children }), innerHTML: children }));
return { html };
}

View file

@ -1,5 +1,5 @@
export default {
name: '@astro-renderer/svelte',
name: '@astrojs/renderer-svelte',
snowpackPlugin: '@snowpack/plugin-svelte',
snowpackPluginOptions: { compilerOptions: { hydratable: true } },
client: './client',

View file

@ -1,5 +1,5 @@
{
"name": "@astro-renderer/svelte",
"name": "@astrojs/renderer-svelte",
"version": "0.0.1",
"type": "module",
"exports": {

View file

@ -1,5 +1,5 @@
export default {
name: '@astro-renderer/vue',
name: '@astrojs/renderer-vue',
snowpackPlugin: '@snowpack/plugin-vue',
client: './client',
server: './server',

View file

@ -1,5 +1,5 @@
{
"name": "@astro-renderer/vue",
"name": "@astrojs/renderer-vue",
"version": "0.0.1",
"type": "module",
"exports": {
@ -11,6 +11,6 @@
"dependencies": {
"vue": "^3.0.10",
"@vue/server-renderer": "^3.0.10",
"@snowpack/plugin-vue": "^2.5.0"
"@snowpack/plugin-vue": "^2.6.0"
}
}

View file

@ -112,7 +112,6 @@
"source.tsx": "typescriptreact",
"source.js": "javascript",
"source.css": "css",
"meta.embedded.block.frontmatter": "yaml",
"meta.embedded.block.css": "css",
"meta.embedded.block.astro": "astro",
"meta.embedded.block.ini": "ini",

View file

@ -3,9 +3,6 @@
"name": "Astro Markdown",
"scopeName": "text.html.markdown.astro",
"patterns": [
{
"include": "#frontMatter"
},
{
"include": "#block"
},
@ -2351,16 +2348,6 @@
"match": "(^|\\G)\\s*([\\*\\-\\_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?",
"name": "meta.separator.markdown"
},
"frontMatter": {
"begin": "\\A-{3}\\s*$",
"contentName": "meta.embedded.block.frontmatter",
"patterns": [
{
"include": "source.yaml"
}
],
"end": "(^|\\G)-{3}|\\.{3}\\s*$"
},
"inline": {
"patterns": [
{

View file

@ -1280,6 +1280,23 @@
dependencies:
"@octokit/openapi-types" "^7.2.0"
"@rollup/plugin-replace@^2.4.2":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a"
integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==
dependencies:
"@rollup/pluginutils" "^3.1.0"
magic-string "^0.25.7"
"@rollup/pluginutils@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
dependencies:
"@types/estree" "0.0.39"
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@silvenon/remark-smartypants@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@silvenon/remark-smartypants/-/remark-smartypants-1.0.0.tgz#0692263f4b22af92caea9382b77bc287db69c5d1"
@ -1321,11 +1338,12 @@
svelte-hmr "^0.13.2"
svelte-preprocess "^4.7.2"
"@snowpack/plugin-vue@^2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@snowpack/plugin-vue/-/plugin-vue-2.5.0.tgz#6912e7406c6d6dd7abec761475b6258a40c6b87a"
integrity sha512-yqoGY9bqKONilEGxiAKi+m854/e/mn5zoU5JR73vDnUJetGePY9xAziIIc7knF/V1sKYYz1kxXIHOD8RPkgujA==
"@snowpack/plugin-vue@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@snowpack/plugin-vue/-/plugin-vue-2.6.0.tgz#9e8ac6eeb15d5e17d3ca297364c140c96d88a667"
integrity sha512-kRjfSHMckf2wwPwpQdgDzxxX637rwC2MZIk9ib6GWlrvbFDAjLozeQMY883naQSu8RwZ4jUC0gW1OHzgz8lLPw==
dependencies:
"@rollup/plugin-replace" "^2.4.2"
"@vue/compiler-sfc" "^3.0.10"
hash-sum "^2.0.0"
@ -1379,6 +1397,11 @@
dependencies:
"@types/babel-types" "*"
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/estree@0.0.46":
version "0.0.46"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe"
@ -3900,6 +3923,11 @@ estree-walker@^0.6.1:
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
estree-walker@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
estree-walker@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
@ -8881,10 +8909,10 @@ smartwrap@^1.2.3:
wcwidth "^1.0.1"
yargs "^15.1.0"
snowpack@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/snowpack/-/snowpack-3.5.1.tgz#51fbdbb56794ebb7a07f3fecd4fc60454e4f366f"
integrity sha512-YOpC3hpGSzFOKgbHfMmWmJ1KYEtdBWkbUUdT0H3E1fsc73qRv97wskNkVaeNKtr/yrImR5+KHUvxcWY6jgHVtw==
snowpack@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/snowpack/-/snowpack-3.5.2.tgz#0b23619be535b22ebdda1ab3eba3444acbf35b91"
integrity sha512-TQQT5PXxeDr4gaMbp6nQrTDLX+Y8G5qI2wLqQdHLrpQEnq7W+gysn94+0xbOhnx0pFoVlSoFPjdQ83sETWl/9A==
dependencies:
cli-spinners "^2.5.0"
default-browser-id "^2.0.0"