Skip to content

Commit

Permalink
Read jsxImportSource from tsconfig (#4759)
Browse files Browse the repository at this point in the history
* Read jsxImportSource from tsconfig

* Only read from tsconfig if not found earlier
  • Loading branch information
matthewp committed Sep 15, 2022
1 parent 0398efa commit fc885ea
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/tricky-cats-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Read jsxImportSource from tsconfig
3 changes: 3 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -876,6 +877,8 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
// 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;
Expand Down
16 changes: 16 additions & 0 deletions packages/astro/src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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: [],
Expand Down Expand Up @@ -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
Expand Down
24 changes: 7 additions & 17 deletions packages/astro/src/vite-plugin-config-alias/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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)
);

Expand Down Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions packages/astro/src/vite-plugin-jsx/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<string, string> = {
react: "import React from 'react'",
Expand Down Expand Up @@ -223,6 +228,12 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
importSource = await detectImportSourceFromImports(code, id, jsxRenderers);
}

// Check the tsconfig
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) {
const [defaultRendererName] = defaultJSXRendererEntry;
Expand Down
7 changes: 7 additions & 0 deletions packages/astro/test/fixtures/react-and-solid/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -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()]
});
8 changes: 8 additions & 0 deletions packages/astro/test/fixtures/react-and-solid/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@test/react-and-solid",
"dependencies": {
"astro": "workspace:*",
"@astrojs/react": "workspace:*",
"@astrojs/solid-js": "workspace:*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @jsxImportSource react */
import { FC } from 'react';

export const ExampleReact: FC = () => {
return <div id="example-react">example react component</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { VoidComponent } from 'solid-js';

export const ExampleSolid: VoidComponent = () => {
return <div id="example-solid">example solidjs component</div>;
};
13 changes: 13 additions & 0 deletions packages/astro/test/fixtures/react-and-solid/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
import { ExampleReact } from '../components/ExampleReact';
import { ExampleSolid } from '../components/ExampleSolid';
---
<html>
<head>
<title>title</title>
</head>
<body>
<ExampleReact />
<ExampleSolid />
</body>
</html>
7 changes: 7 additions & 0 deletions packages/astro/test/fixtures/react-and-solid/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"strict": true,
"jsxImportSource": "solid-js",
"types": ["astro/client"]
}
}
20 changes: 20 additions & 0 deletions packages/astro/test/react-and-solid.test.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
10 changes: 10 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fc885ea

Please sign in to comment.