Read jsxImportSource from tsconfig (#4759)
* Read jsxImportSource from tsconfig * Only read from tsconfig if not found earlier
This commit is contained in:
parent
0398efa39f
commit
fc885eaea1
13 changed files with 118 additions and 17 deletions
5
.changeset/tricky-cats-drop.md
Normal file
5
.changeset/tricky-cats-drop.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Read jsxImportSource from tsconfig
|
|
@ -9,6 +9,7 @@ import type {
|
||||||
} from '@astrojs/markdown-remark';
|
} from '@astrojs/markdown-remark';
|
||||||
import type * as babel from '@babel/core';
|
import type * as babel from '@babel/core';
|
||||||
import type { AddressInfo } from 'net';
|
import type { AddressInfo } from 'net';
|
||||||
|
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||||
import type * as vite from 'vite';
|
import type * as vite from 'vite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import type { SerializedSSRManifest } from '../core/app/types';
|
import type { SerializedSSRManifest } from '../core/app/types';
|
||||||
|
@ -876,6 +877,8 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
|
||||||
// that is different from the user-exposed configuration.
|
// that is different from the user-exposed configuration.
|
||||||
// TODO: Create an AstroConfig class to manage this, long-term.
|
// TODO: Create an AstroConfig class to manage this, long-term.
|
||||||
_ctx: {
|
_ctx: {
|
||||||
|
tsConfig: TsConfigJson | undefined;
|
||||||
|
tsConfigPath: string | undefined;
|
||||||
pageExtensions: string[];
|
pageExtensions: string[];
|
||||||
injectedRoutes: InjectedRoute[];
|
injectedRoutes: InjectedRoute[];
|
||||||
adapter: AstroAdapter | undefined;
|
adapter: AstroAdapter | undefined;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import * as colors from 'kleur/colors';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import postcssrc from 'postcss-load-config';
|
import postcssrc from 'postcss-load-config';
|
||||||
import { BUNDLED_THEMES } from 'shiki';
|
import { BUNDLED_THEMES } from 'shiki';
|
||||||
|
import * as tsr from 'tsconfig-resolver';
|
||||||
import { fileURLToPath, pathToFileURL } from 'url';
|
import { fileURLToPath, pathToFileURL } from 'url';
|
||||||
import * as vite from 'vite';
|
import * as vite from 'vite';
|
||||||
import { mergeConfig as mergeViteConfig } from 'vite';
|
import { mergeConfig as mergeViteConfig } from 'vite';
|
||||||
|
@ -345,11 +346,14 @@ export async function validateConfig(
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.default({}),
|
||||||
});
|
});
|
||||||
|
const tsconfig = loadTSConfig(root);
|
||||||
// First-Pass Validation
|
// First-Pass Validation
|
||||||
const result = {
|
const result = {
|
||||||
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
|
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
|
||||||
_ctx: {
|
_ctx: {
|
||||||
pageExtensions: ['.astro', '.md', '.html'],
|
pageExtensions: ['.astro', '.md', '.html'],
|
||||||
|
tsConfig: tsconfig?.config,
|
||||||
|
tsConfigPath: tsconfig?.path,
|
||||||
scripts: [],
|
scripts: [],
|
||||||
renderers: [jsxRenderer],
|
renderers: [jsxRenderer],
|
||||||
injectedRoutes: [],
|
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
|
* Attempt to load an `astro.config.mjs` file
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as tsr from 'tsconfig-resolver';
|
|
||||||
import * as url from 'url';
|
|
||||||
import type { AstroConfig } from '../@types/astro';
|
import type { AstroConfig } from '../@types/astro';
|
||||||
|
|
||||||
import type * as vite from 'vite';
|
import type * as vite from 'vite';
|
||||||
|
@ -14,33 +12,25 @@ export declare interface Alias {
|
||||||
/** Returns a path with its slashes replaced with posix slashes. */
|
/** Returns a path with its slashes replaced with posix slashes. */
|
||||||
const normalize = (pathname: string) => String(pathname).split(path.sep).join(path.posix.sep);
|
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. */
|
/** 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 */
|
/** 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 no config was found, return null
|
||||||
if (!config) return null;
|
if (!config || !configPath) return null;
|
||||||
|
|
||||||
/** Compiler options from tsconfig.json or jsconfig.json. */
|
/** 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 no compilerOptions.baseUrl was defined, return null
|
||||||
if (!compilerOptions.baseUrl) return null;
|
if (!compilerOptions.baseUrl) return null;
|
||||||
|
|
||||||
// resolve the base url from the configuration file directory
|
// resolve the base url from the configuration file directory
|
||||||
const baseUrl = path.posix.resolve(
|
const baseUrl = path.posix.resolve(
|
||||||
path.posix.dirname(normalize(config.path).replace(/^\/?/, '/')),
|
path.posix.dirname(normalize(configPath).replace(/^\/?/, '/')),
|
||||||
normalize(compilerOptions.baseUrl)
|
normalize(compilerOptions.baseUrl)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -93,7 +83,7 @@ export default function configAliasVitePlugin({
|
||||||
config: AstroConfig;
|
config: AstroConfig;
|
||||||
}): vite.PluginOption {
|
}): vite.PluginOption {
|
||||||
/** Aliases from the tsconfig.json or jsconfig.json configuration. */
|
/** 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 no config alias was found, bypass this plugin
|
||||||
if (!configAlias) return {} as vite.PluginOption;
|
if (!configAlias) return {} as vite.PluginOption;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { TransformResult } from 'rollup';
|
import type { TransformResult } from 'rollup';
|
||||||
|
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||||
import type { Plugin, ResolvedConfig } from 'vite';
|
import type { Plugin, ResolvedConfig } from 'vite';
|
||||||
import type { AstroConfig, AstroRenderer } from '../@types/astro';
|
import type { AstroConfig, AstroRenderer } from '../@types/astro';
|
||||||
import type { LogOptions } from '../core/logger/core.js';
|
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 { parseNpmName } from '../core/util.js';
|
||||||
import tagExportsPlugin from './tag.js';
|
import tagExportsPlugin from './tag.js';
|
||||||
|
|
||||||
|
type FixedCompilerOptions = TsConfigJson.CompilerOptions & {
|
||||||
|
jsxImportSource?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.mdx']);
|
const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.mdx']);
|
||||||
const IMPORT_STATEMENTS: Record<string, string> = {
|
const IMPORT_STATEMENTS: Record<string, string> = {
|
||||||
react: "import React from 'react'",
|
react: "import React from 'react'",
|
||||||
|
@ -223,6 +228,12 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
|
||||||
importSource = await detectImportSourceFromImports(code, id, jsxRenderers);
|
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 we still can’t tell the import source, now is the time to throw an error.
|
||||||
if (!importSource && defaultJSXRendererEntry) {
|
if (!importSource && defaultJSXRendererEntry) {
|
||||||
const [defaultRendererName] = defaultJSXRendererEntry;
|
const [defaultRendererName] = defaultJSXRendererEntry;
|
||||||
|
|
7
packages/astro/test/fixtures/react-and-solid/astro.config.mjs
vendored
Normal file
7
packages/astro/test/fixtures/react-and-solid/astro.config.mjs
vendored
Normal file
|
@ -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
packages/astro/test/fixtures/react-and-solid/package.json
vendored
Normal file
8
packages/astro/test/fixtures/react-and-solid/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/react-and-solid",
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*",
|
||||||
|
"@astrojs/react": "workspace:*",
|
||||||
|
"@astrojs/solid-js": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
6
packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx
vendored
Normal file
6
packages/astro/test/fixtures/react-and-solid/src/components/ExampleReact.tsx
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/** @jsxImportSource react */
|
||||||
|
import { FC } from 'react';
|
||||||
|
|
||||||
|
export const ExampleReact: FC = () => {
|
||||||
|
return <div id="example-react">example react component</div>;
|
||||||
|
};
|
5
packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx
vendored
Normal file
5
packages/astro/test/fixtures/react-and-solid/src/components/ExampleSolid.tsx
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { VoidComponent } from 'solid-js';
|
||||||
|
|
||||||
|
export const ExampleSolid: VoidComponent = () => {
|
||||||
|
return <div id="example-solid">example solidjs component</div>;
|
||||||
|
};
|
13
packages/astro/test/fixtures/react-and-solid/src/pages/index.astro
vendored
Normal file
13
packages/astro/test/fixtures/react-and-solid/src/pages/index.astro
vendored
Normal file
|
@ -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
packages/astro/test/fixtures/react-and-solid/tsconfig.json
vendored
Normal file
7
packages/astro/test/fixtures/react-and-solid/tsconfig.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"jsxImportSource": "solid-js",
|
||||||
|
"types": ["astro/client"]
|
||||||
|
}
|
||||||
|
}
|
20
packages/astro/test/react-and-solid.test.js
Normal file
20
packages/astro/test/react-and-solid.test.js
Normal file
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1748,6 +1748,16 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: link:../../..
|
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:
|
packages/astro/test/fixtures/react-component:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@astrojs/react': workspace:*
|
'@astrojs/react': workspace:*
|
||||||
|
|
Loading…
Reference in a new issue