From 947cbcf1dcb9cf3c821c9acd03916f6a00abf628 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 14 Sep 2022 16:32:05 -0400 Subject: [PATCH 1/2] Read jsxImportSource from tsconfig --- .changeset/tricky-cats-drop.md | 5 ++++ packages/astro/src/@types/astro.ts | 3 +++ packages/astro/src/core/config.ts | 16 +++++++++++++ .../src/vite-plugin-config-alias/index.ts | 24 ++++++------------- packages/astro/src/vite-plugin-jsx/index.ts | 9 +++++++ .../fixtures/react-and-solid/astro.config.mjs | 7 ++++++ .../fixtures/react-and-solid/package.json | 8 +++++++ .../src/components/ExampleReact.tsx | 6 +++++ .../src/components/ExampleSolid.tsx | 5 ++++ .../react-and-solid/src/pages/index.astro | 13 ++++++++++ .../fixtures/react-and-solid/tsconfig.json | 7 ++++++ packages/astro/test/react-and-solid.test.js | 20 ++++++++++++++++ pnpm-lock.yaml | 10 ++++++++ 13 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 .changeset/tricky-cats-drop.md create mode 100644 packages/astro/test/fixtures/react-and-solid/astro.config.mjs create mode 100644 packages/astro/test/fixtures/react-and-solid/package.json create mode 100644 packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx create mode 100644 packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx create mode 100644 packages/astro/test/fixtures/react-and-solid/src/pages/index.astro create mode 100644 packages/astro/test/fixtures/react-and-solid/tsconfig.json create mode 100644 packages/astro/test/react-and-solid.test.js diff --git a/.changeset/tricky-cats-drop.md b/.changeset/tricky-cats-drop.md new file mode 100644 index 000000000000..53ea749bf16b --- /dev/null +++ b/.changeset/tricky-cats-drop.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Read jsxImportSource from tsconfig diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index c358d63e7901..817467a30b79 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -9,6 +9,7 @@ import type { } from '@astrojs/markdown-remark'; import type * as babel from '@babel/core'; import type { AddressInfo } from 'net'; +import type { TsConfigJson } from 'tsconfig-resolver'; import type * as vite from 'vite'; import { z } from 'zod'; import type { SerializedSSRManifest } from '../core/app/types'; @@ -876,6 +877,8 @@ export interface AstroConfig extends z.output { // that is different from the user-exposed configuration. // TODO: Create an AstroConfig class to manage this, long-term. _ctx: { + tsConfig: TsConfigJson | undefined; + tsConfigPath: string | undefined; pageExtensions: string[]; injectedRoutes: InjectedRoute[]; adapter: AstroAdapter | undefined; diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index 611584b76291..3aa820835a6c 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -11,6 +11,7 @@ import * as colors from 'kleur/colors'; import path from 'path'; import postcssrc from 'postcss-load-config'; import { BUNDLED_THEMES } from 'shiki'; +import * as tsr from 'tsconfig-resolver'; import { fileURLToPath, pathToFileURL } from 'url'; import * as vite from 'vite'; import { mergeConfig as mergeViteConfig } from 'vite'; @@ -345,11 +346,14 @@ export async function validateConfig( .optional() .default({}), }); + const tsconfig = loadTSConfig(root); // First-Pass Validation const result = { ...(await AstroConfigRelativeSchema.parseAsync(userConfig)), _ctx: { pageExtensions: ['.astro', '.md', '.html'], + tsConfig: tsconfig?.config, + tsConfigPath: tsconfig?.path, scripts: [], renderers: [jsxRenderer], injectedRoutes: [], @@ -550,6 +554,18 @@ async function tryLoadConfig( } } +function loadTSConfig( + cwd: string | undefined +): tsr.TsConfigResult | undefined { + for(const searchName of ['tsconfig.json', 'jsconfig.json']) { + const config = tsr.tsconfigResolverSync({ cwd, searchName }); + if(config.exists) { + return config; + } + } + return undefined +} + /** * Attempt to load an `astro.config.mjs` file * @deprecated diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index 782cacc411f1..62f5abd989b1 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -1,6 +1,4 @@ import * as path from 'path'; -import * as tsr from 'tsconfig-resolver'; -import * as url from 'url'; import type { AstroConfig } from '../@types/astro'; import type * as vite from 'vite'; @@ -14,33 +12,25 @@ export declare interface Alias { /** Returns a path with its slashes replaced with posix slashes. */ const normalize = (pathname: string) => String(pathname).split(path.sep).join(path.posix.sep); -/** Returns the results of a config file if it exists, otherwise null. */ -const getExistingConfig = ( - searchName: string, - cwd: string | undefined -): tsr.TsConfigResultSuccess | null => { - const config = tsr.tsconfigResolverSync({ cwd, searchName }); - - return config.exists ? config : null; -}; /** Returns a list of compiled aliases. */ -const getConfigAlias = (cwd: string | undefined): Alias[] | null => { +const getConfigAlias = (astroConfig: AstroConfig): Alias[] | null => { /** Closest tsconfig.json or jsconfig.json */ - const config = getExistingConfig('tsconfig.json', cwd) || getExistingConfig('jsconfig.json', cwd); + const config = astroConfig._ctx.tsConfig; + const configPath = astroConfig._ctx.tsConfigPath; // if no config was found, return null - if (!config) return null; + if (!config || !configPath) return null; /** Compiler options from tsconfig.json or jsconfig.json. */ - const compilerOptions = Object(config.config.compilerOptions); + const compilerOptions = Object(config.compilerOptions); // if no compilerOptions.baseUrl was defined, return null if (!compilerOptions.baseUrl) return null; // resolve the base url from the configuration file directory const baseUrl = path.posix.resolve( - path.posix.dirname(normalize(config.path).replace(/^\/?/, '/')), + path.posix.dirname(normalize(configPath).replace(/^\/?/, '/')), normalize(compilerOptions.baseUrl) ); @@ -93,7 +83,7 @@ export default function configAliasVitePlugin({ config: AstroConfig; }): vite.PluginOption { /** Aliases from the tsconfig.json or jsconfig.json configuration. */ - const configAlias = getConfigAlias(astroConfig.root && url.fileURLToPath(astroConfig.root)); + const configAlias = getConfigAlias(astroConfig); // if no config alias was found, bypass this plugin if (!configAlias) return {} as vite.PluginOption; diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts index 3c166bf98f77..4ed7c2a5b578 100644 --- a/packages/astro/src/vite-plugin-jsx/index.ts +++ b/packages/astro/src/vite-plugin-jsx/index.ts @@ -1,4 +1,5 @@ import type { TransformResult } from 'rollup'; +import type { TsConfigJson } from 'tsconfig-resolver'; import type { Plugin, ResolvedConfig } from 'vite'; import type { AstroConfig, AstroRenderer } from '../@types/astro'; import type { LogOptions } from '../core/logger/core.js'; @@ -13,6 +14,10 @@ import { error } from '../core/logger/core.js'; import { parseNpmName } from '../core/util.js'; import tagExportsPlugin from './tag.js'; +type FixedCompilerOptions = TsConfigJson.CompilerOptions & { + jsxImportSource?: string; +} + const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.mdx']); const IMPORT_STATEMENTS: Record = { react: "import React from 'react'", @@ -223,6 +228,10 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin importSource = await detectImportSourceFromImports(code, id, jsxRenderers); } + // Check the tsconfig + const compilerOptions = config._ctx.tsConfig?.compilerOptions; + importSource = (compilerOptions as FixedCompilerOptions | undefined)?.jsxImportSource; + // if we still can’t tell the import source, now is the time to throw an error. if (!importSource && defaultJSXRendererEntry) { const [defaultRendererName] = defaultJSXRendererEntry; diff --git a/packages/astro/test/fixtures/react-and-solid/astro.config.mjs b/packages/astro/test/fixtures/react-and-solid/astro.config.mjs new file mode 100644 index 000000000000..f7e79331e958 --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; +import solid from '@astrojs/solid-js'; + +export default defineConfig({ + integrations: [react(), solid()] +}); diff --git a/packages/astro/test/fixtures/react-and-solid/package.json b/packages/astro/test/fixtures/react-and-solid/package.json new file mode 100644 index 000000000000..0ec8ac57ec5d --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/react-and-solid", + "dependencies": { + "astro": "workspace:*", + "@astrojs/react": "workspace:*", + "@astrojs/solid-js": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx b/packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx new file mode 100644 index 000000000000..f4b8cfd55cbd --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx @@ -0,0 +1,6 @@ +/** @jsxImportSource react */ +import { FC } from 'react'; + +export const ExampleReact: FC = () => { + return
example react component
; +}; diff --git a/packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx b/packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx new file mode 100644 index 000000000000..c0e6336fc16b --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx @@ -0,0 +1,5 @@ +import { VoidComponent } from 'solid-js'; + +export const ExampleSolid: VoidComponent = () => { + return
example solidjs component
; +}; diff --git a/packages/astro/test/fixtures/react-and-solid/src/pages/index.astro b/packages/astro/test/fixtures/react-and-solid/src/pages/index.astro new file mode 100644 index 000000000000..99f781534d25 --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/src/pages/index.astro @@ -0,0 +1,13 @@ +--- +import { ExampleReact } from '../components/ExampleReact'; +import { ExampleSolid } from '../components/ExampleSolid'; +--- + + + title + + + + + + diff --git a/packages/astro/test/fixtures/react-and-solid/tsconfig.json b/packages/astro/test/fixtures/react-and-solid/tsconfig.json new file mode 100644 index 000000000000..0b4b89bbce69 --- /dev/null +++ b/packages/astro/test/fixtures/react-and-solid/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "strict": true, + "jsxImportSource": "solid-js", + "types": ["astro/client"] + } +} diff --git a/packages/astro/test/react-and-solid.test.js b/packages/astro/test/react-and-solid.test.js new file mode 100644 index 000000000000..e15e6e7fa061 --- /dev/null +++ b/packages/astro/test/react-and-solid.test.js @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +describe('Solid app with some React components', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ root: './fixtures/react-and-solid/' }); + await fixture.build(); + }); + + it('Reads jsxImportSource from tsconfig', async () => { + let html = await fixture.readFile('/index.html'); + let $ = cheerio.load(html); + expect($('#example-solid').text()).to.equal('example solidjs component'); + expect($('#example-react').text()).to.equal('example react component'); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 649da20d01ed..f011242cbff0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1748,6 +1748,16 @@ importers: dependencies: astro: link:../../.. + packages/astro/test/fixtures/react-and-solid: + specifiers: + '@astrojs/react': workspace:* + '@astrojs/solid-js': workspace:* + astro: workspace:* + dependencies: + '@astrojs/react': link:../../../../integrations/react + '@astrojs/solid-js': link:../../../../integrations/solid + astro: link:../../.. + packages/astro/test/fixtures/react-component: specifiers: '@astrojs/react': workspace:* From 7c6c8e193a693cee955d4568c5de701cba5d1574 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 14 Sep 2022 16:33:41 -0400 Subject: [PATCH 2/2] Only read from tsconfig if not found earlier --- packages/astro/src/vite-plugin-jsx/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/vite-plugin-jsx/index.ts b/packages/astro/src/vite-plugin-jsx/index.ts index 4ed7c2a5b578..9f42b0fa9080 100644 --- a/packages/astro/src/vite-plugin-jsx/index.ts +++ b/packages/astro/src/vite-plugin-jsx/index.ts @@ -229,8 +229,10 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin } // Check the tsconfig - const compilerOptions = config._ctx.tsConfig?.compilerOptions; - importSource = (compilerOptions as FixedCompilerOptions | undefined)?.jsxImportSource; + if(!importSource) { + const compilerOptions = config._ctx.tsConfig?.compilerOptions; + importSource = (compilerOptions as FixedCompilerOptions | undefined)?.jsxImportSource; + } // if we still can’t tell the import source, now is the time to throw an error. if (!importSource && defaultJSXRendererEntry) {