From 619bd409adc996406bd9868f4f88689f54ec98aa Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 4 Aug 2022 15:12:09 -0400 Subject: [PATCH 1/4] Add test --- packages/astro/src/vite-plugin-jsx/index.ts | 3 +++ .../fixtures/mdx-plus-react/astro.config.mjs | 6 +++++ .../test/fixtures/mdx-plus-react/package.json | 8 ++++++ .../src/components/Component.jsx | 5 ++++ .../mdx-plus-react/src/pages/index.astro | 11 ++++++++ .../mdx/test/mdx-plus-react.test.js | 26 +++++++++++++++++++ pnpm-lock.yaml | 10 +++++++ 7 files changed, 69 insertions(+) create mode 100644 packages/integrations/mdx/test/fixtures/mdx-plus-react/astro.config.mjs create mode 100644 packages/integrations/mdx/test/fixtures/mdx-plus-react/package.json create mode 100644 packages/integrations/mdx/test/fixtures/mdx-plus-react/src/components/Component.jsx create mode 100644 packages/integrations/mdx/test/fixtures/mdx-plus-react/src/pages/index.astro create mode 100644 packages/integrations/mdx/test/mdx-plus-react.test.js diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts index 56005413e326..797b7362cf30 100644 --- a/packages/astro/src/vite-plugin-jsx/index.ts +++ b/packages/astro/src/vite-plugin-jsx/index.ts @@ -69,6 +69,9 @@ async function transformJSX({ babelrc: false, inputSourceMap: options.inputSourceMap, }); + + console.log("AFTER", result?.code); + // TODO: Be more strict about bad return values here. // Should we throw an error instead? Should we never return `{code: ""}`? if (!result) return null; diff --git a/packages/integrations/mdx/test/fixtures/mdx-plus-react/astro.config.mjs b/packages/integrations/mdx/test/fixtures/mdx-plus-react/astro.config.mjs new file mode 100644 index 000000000000..4671227d3ea1 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-plus-react/astro.config.mjs @@ -0,0 +1,6 @@ +import mdx from '@astrojs/mdx'; +import react from '@astrojs/react'; + +export default { + integrations: [react(), mdx()] +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-plus-react/package.json b/packages/integrations/mdx/test/fixtures/mdx-plus-react/package.json new file mode 100644 index 000000000000..982f4c685486 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-plus-react/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/mdx-plus-react", + "dependencies": { + "astro": "workspace:*", + "@astrojs/mdx": "workspace:*", + "@astrojs/react": "workspace:*" + } +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/components/Component.jsx b/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/components/Component.jsx new file mode 100644 index 000000000000..53f5dad3f108 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/components/Component.jsx @@ -0,0 +1,5 @@ +const Component = () => { + return

Hello world

; +}; + +export default Component; diff --git a/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/pages/index.astro b/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/pages/index.astro new file mode 100644 index 000000000000..2486e78342be --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-plus-react/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +import Component from "../components/Component.jsx"; +--- + + + Testing + + + + + diff --git a/packages/integrations/mdx/test/mdx-plus-react.test.js b/packages/integrations/mdx/test/mdx-plus-react.test.js new file mode 100644 index 000000000000..abd55b64b66e --- /dev/null +++ b/packages/integrations/mdx/test/mdx-plus-react.test.js @@ -0,0 +1,26 @@ +import mdx from '@astrojs/mdx'; + +import { expect } from 'chai'; +import { parseHTML } from 'linkedom'; +import { loadFixture } from '../../../astro/test/test-utils.js'; + +describe('MDX and React', () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: new URL('./fixtures/mdx-plus-react/', import.meta.url), + }); + await fixture.build(); + }); + + it('can be used in the same project', async () => { + const html = await fixture.readFile('/index.html'); + console.log(html); + /*const { document } = parseHTML(html); + + const h1 = document.querySelector('h1'); + + expect(h1.textContent).to.equal('Hello page!');*/ + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20db5c7de63e..bf0f3729aa5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2188,6 +2188,16 @@ importers: '@astrojs/mdx': link:../../.. astro: link:../../../../../astro + packages/integrations/mdx/test/fixtures/mdx-plus-react: + specifiers: + '@astrojs/mdx': workspace:* + '@astrojs/react': workspace:* + astro: workspace:* + dependencies: + '@astrojs/mdx': link:../../.. + '@astrojs/react': link:../../../../react + astro: link:../../../../../astro + packages/integrations/netlify: specifiers: '@astrojs/webapi': ^0.12.0 From 14d6dea7109fbcb84d8850d785d2208f47a745d9 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 4 Aug 2022 16:11:18 -0400 Subject: [PATCH 2/4] Allow automatic JSX runtime with JSX --- .changeset/slow-frogs-hope.md | 6 ++++++ packages/astro/src/jsx/babel.ts | 8 ++++++++ packages/astro/src/jsx/renderer.ts | 3 +-- packages/astro/src/vite-plugin-jsx/index.ts | 19 ++++++++++++++----- packages/astro/src/vite-plugin-jsx/tag.ts | 2 +- .../mdx/test/mdx-plus-react.test.js | 7 +++---- 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 .changeset/slow-frogs-hope.md diff --git a/.changeset/slow-frogs-hope.md b/.changeset/slow-frogs-hope.md new file mode 100644 index 000000000000..b8214a57fc81 --- /dev/null +++ b/.changeset/slow-frogs-hope.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/mdx': patch +--- + +Allow automatic JSX runtime with MDX diff --git a/packages/astro/src/jsx/babel.ts b/packages/astro/src/jsx/babel.ts index 8850dadc1ac3..ce1587a0bfad 100644 --- a/packages/astro/src/jsx/babel.ts +++ b/packages/astro/src/jsx/babel.ts @@ -136,6 +136,7 @@ export default function astroJSX(): PluginObj { visitor: { Program: { enter(path, state) { + const { file } = state; if (!(state.file.metadata as PluginMetadata).astro) { (state.file.metadata as PluginMetadata).astro = { clientOnlyComponents: [], @@ -143,6 +144,13 @@ export default function astroJSX(): PluginObj { scripts: [], }; } + if(!file.ast.comments) { + file.ast.comments = []; + } + file.ast.comments?.push({ + type: 'CommentBlock', + value: '* @jsxImportSource astro ' + }); path.node.body.splice( 0, 0, diff --git a/packages/astro/src/jsx/renderer.ts b/packages/astro/src/jsx/renderer.ts index 3aee8520fc64..d40c5200e0b6 100644 --- a/packages/astro/src/jsx/renderer.ts +++ b/packages/astro/src/jsx/renderer.ts @@ -1,7 +1,6 @@ const renderer = { name: 'astro:jsx', serverEntrypoint: 'astro/jsx/server.js', - jsxImportSource: 'astro', jsxTransformOptions: async () => { const { default: { default: jsx }, @@ -11,7 +10,7 @@ const renderer = { return { plugins: [ astroJSX(), - jsx({}, { throwIfNamespace: false, runtime: 'automatic', importSource: 'astro' }), + jsx({}, { throwIfNamespace: false, runtime: 'automatic' }), ], }; }, diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts index 797b7362cf30..a217fcbb97b6 100644 --- a/packages/astro/src/vite-plugin-jsx/index.ts +++ b/packages/astro/src/vite-plugin-jsx/index.ts @@ -14,6 +14,7 @@ import { parseNpmName } from '../core/util.js'; import tagExportsPlugin from './tag.js'; const JSX_RENDERER_CACHE = new WeakMap>(); +const JSX_RENDERER_WITH_AUTO_CACHE = new WeakMap(); const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.mdx']); const IMPORT_STATEMENTS: Record = { react: "import React from 'react'", @@ -33,7 +34,7 @@ function getEsbuildLoader(fileExt: string): string { } function collectJSXRenderers(renderers: AstroRenderer[]): Map { - const renderersWithJSXSupport = renderers.filter((r) => r.jsxImportSource); + const renderersWithJSXSupport = renderers.filter((r) => r.jsxImportSource || r.jsxTransformOptions); return new Map( renderersWithJSXSupport.map((r) => [r.jsxImportSource, r] as [string, AstroRenderer]) ); @@ -70,8 +71,6 @@ async function transformJSX({ inputSourceMap: options.inputSourceMap, }); - console.log("AFTER", result?.code); - // TODO: Be more strict about bad return values here. // Should we throw an error instead? Should we never return `{code: ""}`? if (!result) return null; @@ -121,6 +120,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin const { mode } = viteConfig; let jsxRenderers = JSX_RENDERER_CACHE.get(config); + let jsxAutoRenderers = JSX_RENDERER_WITH_AUTO_CACHE.get(config); // load renderers (on first run only) if (!jsxRenderers) { @@ -139,10 +139,19 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin } JSX_RENDERER_CACHE.set(config, jsxRenderers); } + if(!jsxAutoRenderers) { + jsxAutoRenderers = []; + for(const [,renderer] of jsxRenderers) { + if(renderer.jsxImportSource) { + jsxAutoRenderers.push(renderer); + } + } + } // Attempt: Single JSX renderer // If we only have one renderer, we can skip a bunch of work! - if (jsxRenderers.size === 1) { + if (jsxAutoRenderers.length === 1 || jsxRenderers.size === 1) { + const renderer = jsxAutoRenderers.length ? jsxAutoRenderers[0] : Array.from(jsxRenderers.values())[0]; // downlevel any non-standard syntax, but preserve JSX const { code: jsxCode } = await esbuild.transform(code, { loader: getEsbuildLoader(path.extname(id)) as esbuild.Loader, @@ -153,7 +162,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin return transformJSX({ code: jsxCode, id, - renderer: [...jsxRenderers.values()][0], + renderer, mode, ssr, }); diff --git a/packages/astro/src/vite-plugin-jsx/tag.ts b/packages/astro/src/vite-plugin-jsx/tag.ts index 12bb3bcdd5b7..5a0040d20eb9 100644 --- a/packages/astro/src/vite-plugin-jsx/tag.ts +++ b/packages/astro/src/vite-plugin-jsx/tag.ts @@ -20,7 +20,7 @@ export default function tagExportsWithRenderer({ // Inject `import { __astro_tag_component__ } from 'astro/server/index.js'` enter(path) { path.node.body.splice( - 0, + 1, 0, t.importDeclaration( [ diff --git a/packages/integrations/mdx/test/mdx-plus-react.test.js b/packages/integrations/mdx/test/mdx-plus-react.test.js index abd55b64b66e..49c25d558dde 100644 --- a/packages/integrations/mdx/test/mdx-plus-react.test.js +++ b/packages/integrations/mdx/test/mdx-plus-react.test.js @@ -16,11 +16,10 @@ describe('MDX and React', () => { it('can be used in the same project', async () => { const html = await fixture.readFile('/index.html'); - console.log(html); - /*const { document } = parseHTML(html); + const { document } = parseHTML(html); - const h1 = document.querySelector('h1'); + const p = document.querySelector('p'); - expect(h1.textContent).to.equal('Hello page!');*/ + expect(p.textContent).to.equal('Hello world'); }); }); From 6c000abce951317d778086f16b963ed54f670774 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 4 Aug 2022 16:19:35 -0400 Subject: [PATCH 3/4] fix weirdness --- packages/integrations/react/src/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts index a283938c343f..d8ff44392f24 100644 --- a/packages/integrations/react/src/index.ts +++ b/packages/integrations/react/src/index.ts @@ -12,10 +12,9 @@ function getRenderer() { : '@astrojs/react/server-v17.js', jsxImportSource: 'react', jsxTransformOptions: async () => { - const { - default: { default: jsx }, - // @ts-expect-error types not found - } = await import('@babel/plugin-transform-react-jsx'); + // @ts-expect-error types not found + const babelPluginTransformReactJsxModule = await import('@babel/plugin-transform-react-jsx'); + const jsx = babelPluginTransformReactJsxModule?.default ?? babelPluginTransformReactJsxModule?.default?.default; return { plugins: [ jsx( From 258aac045c772c620e7088ccecd0ef500c78ed05 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 4 Aug 2022 16:28:06 -0400 Subject: [PATCH 4/4] Should be right now --- packages/integrations/react/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts index d8ff44392f24..4ae6ea77fc7b 100644 --- a/packages/integrations/react/src/index.ts +++ b/packages/integrations/react/src/index.ts @@ -14,7 +14,7 @@ function getRenderer() { jsxTransformOptions: async () => { // @ts-expect-error types not found const babelPluginTransformReactJsxModule = await import('@babel/plugin-transform-react-jsx'); - const jsx = babelPluginTransformReactJsxModule?.default ?? babelPluginTransformReactJsxModule?.default?.default; + const jsx = babelPluginTransformReactJsxModule?.default?.default ?? babelPluginTransformReactJsxModule?.default; return { plugins: [ jsx(