Minor change to jsxTransformOptions, update Renderer API docs (#1630)

This commit is contained in:
Drew Powers 2021-10-22 11:55:02 -06:00 committed by Drew Powers
parent b0ef391e57
commit 2c15795607
5 changed files with 53 additions and 29 deletions

View file

@ -0,0 +1,11 @@
---
'astro': patch
'@astrojs/renderer-lit': patch
'@astrojs/renderer-preact': patch
'@astrojs/renderer-react': patch
'@astrojs/renderer-solid': patch
'@astrojs/renderer-svelte': patch
'@astrojs/renderer-vue': patch
---
Improve renderer APIs for Vite

View file

@ -49,22 +49,32 @@ This means that Astro users don't need to install the UI framework packages them
The main entrypoint of a renderer is a simple JS file which exports a manifest for the renderer. The required values are `name`, `server`, and `client`.
Additionally, this entrypoint can define a [Snowpack plugin](https://www.snowpack.dev/guides/plugins) that should be used to load non-JavaScript files.
Additionally, this entrypoint can define a [Vite config object](https://vitejs.dev/config/) that should be used to load non-JavaScript files.
```js
import myVitePlugin from 'vite-plugin-myplugin';
export default {
name: '@astrojs/renderer-xxx', // the renderer name
client: './client.js', // relative path to the client entrypoint
server: './server.js', // optional, relative path to the server entrypoint
snowpackPlugin: '@snowpack/plugin-xxx', // optional, the name of a snowpack plugin to inject
snowpackPluginOptions: { example: true }, // optional, any options to be forwarded to the snowpack plugin
knownEntrypoints: ['framework'], // optional, entrypoint modules that will be used by compiled source
external: ['dep'], // optional, dependencies that should not be built by snowpack
polyfills: ['./shadow-dom-polyfill.js'], // optional, module scripts that should be loaded before client hydration.
hydrationPolyfills: ['./hydrate-framework.js'], // optional, polyfills that need to run before hydration ever occurs.
jsxImportSource: 'preact', // optional, the name of the library from which JSX is imported
jsxTransformOptions: async () => {
// optional, a function to transform JSX files
server: './server.js', // (optional) relative path to the server entrypoint
viteConfig(options = { mode: 'development', command: 'serve' }) {
// (optional) return config object for Vite (https://vitejs.dev/config/)
return {
plugins: [myVitePlugin()], // tip: usually this will depend on a Vite plugin
optimizeDeps: {
include: ['my-client-dep'], // tip: its always a good idea to specify any client-side hydration deps here
},
ssr: {
external: ['my-client-dep/node/server.js'], // tip: use ssr.external in case you encounter code meant only for Node
},
};
},
polyfills: ['./shadow-dom-polyfill.js'], // (optional) scripts that should be injected on the page for the component
hydrationPolyfills: ['./hydrate-framework.js'], // (optional) polyfills that need to run before hydration ever occurs
jsxImportSource: 'preact', // (optional) the name of the library from which JSX is imported ("react", "preact", "solid-js", etc.)
jsxTransformOptions: async (options = { mode: 'development', ssr: true }) => {
// (optional) a function to transform JSX files
const {
default: { default: jsx },
} = await import('@babel/plugin-transform-react-jsx');
@ -72,7 +82,6 @@ export default {
plugins: [jsx({}, { runtime: 'automatic', importSource: 'preact' })],
};
},
vitePlugins: [], // optional, inject Vite plugins here (https://vitejs.dev/plugins/#plugins)
};
```
@ -92,19 +101,15 @@ This is an `async` function that returns information about how to transform matc
> Keep in mind that this transform doesn't need to handle TSX separately from JSX, Astro handles that for you!
The arguments passed to `jsxTransformOptions` follow Snowpack's `load()` plugin hook. These allow you to pass separate Babel configurations for various conditions, like if your files should be compiled differently in SSR mode.
`jsxTransformOptions` receives context about whether its running in `development` or `production` mode, as well as whether or not its running in SSR or client hydration. These allow you to pass separate Babel configurations for various conditions, like if your files should be compiled differently in SSR mode.
```ts
export interface JSXTransformOptions {
(context: {
/** True if builder is in dev mode (`astro dev`) */
isDev: boolean;
/** True if HMR is enabled (add any HMR code to the output here). */
isHmrEnabled: boolean;
/** "development" or "production" */
mode: string;
/** True if builder is in SSR mode */
isSSR: boolean;
/** True if file being transformed is inside of a package. */
isPackage: boolean;
ssr: boolean;
}) => {
plugins?: any[];
presets?: any[];

View file

@ -162,7 +162,7 @@ export interface JSXTransformConfig {
plugins?: babel.PluginItem[];
}
export type JSXTransformFn = (options: { isSSR: boolean }) => Promise<JSXTransformConfig>;
export type JSXTransformFn = (options: { mode: string; ssr: boolean }) => Promise<JSXTransformConfig>;
export interface ManifestData {
routes: RouteData[];

View file

@ -1,5 +1,5 @@
import type { TransformResult } from 'rollup';
import type { Plugin } from '../core/vite';
import type { Plugin, ResolvedConfig } from '../core/vite';
import type { AstroConfig, Renderer } from '../@types/astro-core';
import type { LogOptions } from '../core/logger';
@ -43,15 +43,22 @@ function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
/** Use Astro config to allow for alternate or multiple JSX renderers (by default Vite will assume React) */
export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin {
let viteConfig: ResolvedConfig;
return {
name: '@astrojs/vite-plugin-jsx',
enforce: 'pre', // run transforms before other plugins
configResolved(resolvedConfig) {
viteConfig = resolvedConfig;
},
async transform(code, id, ssrOrOptions) {
const ssr = isSSR(ssrOrOptions);
if (!JSX_EXTENSIONS.has(path.extname(id))) {
return null;
}
const { mode } = viteConfig;
// load renderers (on first run only)
if (JSX_RENDERERS.size === 0) {
const jsxRenderers = await loadJSXRenderers(config.renderers);
@ -76,7 +83,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
loader: getLoader(path.extname(id)),
jsx: 'preserve',
});
return transformJSX({ code: jsxCode, id, renderer: [...JSX_RENDERERS.values()][0], ssr });
return transformJSX({ code: jsxCode, id, renderer: [...JSX_RENDERERS.values()][0], mode, ssr });
}
// Attempt: Multiple JSX renderers
@ -130,7 +137,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
loader: getLoader(path.extname(id)),
jsx: 'preserve',
});
return transformJSX({ code: jsxCode, id, renderer: JSX_RENDERERS.get(importSource) as Renderer, ssr });
return transformJSX({ code: jsxCode, id, renderer: JSX_RENDERERS.get(importSource) as Renderer, mode, ssr });
}
// if we still cant tell, throw error
@ -170,14 +177,15 @@ async function loadJSXRenderers(rendererNames: string[]): Promise<Map<string, Re
interface TransformJSXOptions {
code: string;
id: string;
ssr: boolean;
mode: string;
renderer: Renderer; // note MUST check for JSX beforehand!
ssr: boolean;
}
/** Transform JSX with Babel */
async function transformJSX({ code, id, ssr, renderer }: TransformJSXOptions): Promise<TransformResult> {
async function transformJSX({ code, mode, id, ssr, renderer }: TransformJSXOptions): Promise<TransformResult> {
const { jsxTransformOptions } = renderer;
const options = await jsxTransformOptions!({ isSSR: ssr || false }); // must filter for this beforehand
const options = await jsxTransformOptions!({ mode, ssr }); // must filter for this beforehand
const result = await new Promise<babel.BabelFileResult | null>((resolve, reject) => {
const plugins = [...(options.plugins || [])];
babel.transform(

View file

@ -3,14 +3,14 @@ export default {
client: './client.js',
server: './server.js',
jsxImportSource: 'solid-js',
jsxTransformOptions: async ({ isSSR }) => {
jsxTransformOptions: async ({ ssr }) => {
const [{ default: solid }] = await Promise.all([import('babel-preset-solid')]);
const options = {
presets: [solid({}, { generate: isSSR ? 'ssr' : 'dom', hydratable: true })],
plugins: [],
};
if (isSSR) {
if (ssr) {
options.plugins.push([
'babel-plugin-module-resolver',
{