Compare commits
1 commit
feat/tscon
...
main
Author | SHA1 | Date | |
---|---|---|---|
993c085952 |
19 changed files with 132 additions and 204 deletions
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixed `tsconfig.json`'s new array format for `extends` not working
|
|
@ -167,7 +167,7 @@
|
|||
"shiki": "^0.14.3",
|
||||
"string-width": "^6.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tsconfck": "3.0.0-next.9",
|
||||
"tsconfig-resolver": "^3.0.1",
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"vfile": "^5.3.7",
|
||||
"vite": "^4.4.9",
|
||||
|
|
|
@ -11,13 +11,13 @@ import type * as babel from '@babel/core';
|
|||
import type { OutgoingHttpHeaders } from 'node:http';
|
||||
import type { AddressInfo } from 'node:net';
|
||||
import type * as rollup from 'rollup';
|
||||
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||
import type * as vite from 'vite';
|
||||
import type { RemotePattern } from '../assets/utils/remotePattern.js';
|
||||
import type { SerializedSSRManifest } from '../core/app/types.js';
|
||||
import type { PageBuildData } from '../core/build/types.js';
|
||||
import type { AstroConfigType } from '../core/config/index.js';
|
||||
import type { AstroTimer } from '../core/config/timer.js';
|
||||
import type { TSConfig } from '../core/config/tsconfig.js';
|
||||
import type { AstroCookies } from '../core/cookies/index.js';
|
||||
import type { ResponseWithEncoding } from '../core/endpoint/index.js';
|
||||
import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger/core.js';
|
||||
|
@ -1503,7 +1503,7 @@ export interface AstroSettings {
|
|||
* Map of directive name (e.g. `load`) to the directive script code
|
||||
*/
|
||||
clientDirectives: Map<string, string>;
|
||||
tsConfig: TSConfig | undefined;
|
||||
tsConfig: TsConfigJson | undefined;
|
||||
tsConfigPath: string | undefined;
|
||||
watchFiles: string[];
|
||||
timer: AstroTimer;
|
||||
|
|
|
@ -848,31 +848,25 @@ async function updateTSConfig(
|
|||
return UpdateResult.none;
|
||||
}
|
||||
|
||||
let inputConfig = await loadTSConfig(cwd);
|
||||
let inputConfigText = '';
|
||||
const inputConfig = loadTSConfig(cwd, false);
|
||||
const configFileName = inputConfig.exists ? inputConfig.path.split('/').pop() : 'tsconfig.json';
|
||||
|
||||
if (inputConfig === 'invalid-config' || inputConfig === 'unknown-error') {
|
||||
if (inputConfig.reason === 'invalid-config') {
|
||||
return UpdateResult.failure;
|
||||
} else if (inputConfig === 'missing-config') {
|
||||
logger.debug('add', "Couldn't find tsconfig.json or jsconfig.json, generating one");
|
||||
inputConfig = {
|
||||
tsconfig: defaultTSConfig,
|
||||
tsconfigFile: path.join(cwd, 'tsconfig.json'),
|
||||
rawConfig: { tsconfig: defaultTSConfig, tsconfigFile: path.join(cwd, 'tsconfig.json') },
|
||||
};
|
||||
} else {
|
||||
inputConfigText = JSON.stringify(inputConfig.rawConfig.tsconfig, null, 2);
|
||||
}
|
||||
|
||||
const configFileName = inputConfig.tsconfigFile.split('/').pop();
|
||||
if (inputConfig.reason === 'not-found') {
|
||||
logger.debug('add', "Couldn't find tsconfig.json or jsconfig.json, generating one");
|
||||
}
|
||||
|
||||
const outputConfig = updateTSConfigForFramework(
|
||||
inputConfig.rawConfig.tsconfig,
|
||||
inputConfig.exists ? inputConfig.config : defaultTSConfig,
|
||||
firstIntegrationWithTSSettings
|
||||
);
|
||||
|
||||
const input = inputConfig.exists ? JSON.stringify(inputConfig.config, null, 2) : '';
|
||||
const output = JSON.stringify(outputConfig, null, 2);
|
||||
const diff = getDiffContent(inputConfigText, output);
|
||||
const diff = getDiffContent(input, output);
|
||||
|
||||
if (!diff) {
|
||||
return UpdateResult.none;
|
||||
|
@ -912,7 +906,7 @@ async function updateTSConfig(
|
|||
}
|
||||
|
||||
if (await askToContinue({ flags })) {
|
||||
await fs.writeFile(inputConfig.tsconfigFile, output, {
|
||||
await fs.writeFile(inputConfig?.path ?? path.join(cwd, 'tsconfig.json'), output, {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
logger.debug('add', `Updated ${configFileName} file`);
|
||||
|
|
|
@ -35,7 +35,7 @@ export function getViteConfig(inlineConfig: UserConfig) {
|
|||
level: 'info',
|
||||
});
|
||||
const { astroConfig: config } = await resolveConfig({}, cmd);
|
||||
const settings = await createSettings(config, inlineConfig.root);
|
||||
const settings = createSettings(config, inlineConfig.root);
|
||||
await runHookConfigSetup({ settings, command: cmd, logger });
|
||||
const viteConfig = await createVite(
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ export async function attachContentServerListeners({
|
|||
contentPaths.contentDir.href.replace(settings.config.root.href, '')
|
||||
)} for changes`
|
||||
);
|
||||
const maybeTsConfigStats = await getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings });
|
||||
const maybeTsConfigStats = getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings });
|
||||
if (maybeTsConfigStats) warnAllowJsIsFalse({ ...maybeTsConfigStats, logger });
|
||||
await attachListeners();
|
||||
} else {
|
||||
|
@ -96,7 +96,7 @@ See ${bold('https://www.typescriptlang.org/tsconfig#allowJs')} for more informat
|
|||
);
|
||||
}
|
||||
|
||||
async function getTSConfigStatsWhenAllowJsFalse({
|
||||
function getTSConfigStatsWhenAllowJsFalse({
|
||||
contentPaths,
|
||||
settings,
|
||||
}: {
|
||||
|
@ -108,15 +108,15 @@ async function getTSConfigStatsWhenAllowJsFalse({
|
|||
);
|
||||
if (!isContentConfigJsFile) return;
|
||||
|
||||
const inputConfig = await loadTSConfig(fileURLToPath(settings.config.root));
|
||||
if (typeof inputConfig === 'string') return;
|
||||
|
||||
const tsConfigFileName = inputConfig.tsconfigFile.split(path.sep).pop();
|
||||
const inputConfig = loadTSConfig(fileURLToPath(settings.config.root), false);
|
||||
const tsConfigFileName = inputConfig.exists && inputConfig.path.split(path.sep).pop();
|
||||
if (!tsConfigFileName) return;
|
||||
|
||||
const contentConfigFileName = contentPaths.config.url.pathname.split(path.sep).pop()!;
|
||||
const allowJSOption = inputConfig.tsconfig.compilerOptions?.allowJs;
|
||||
if (allowJSOption) return;
|
||||
const allowJSOption = inputConfig?.config?.compilerOptions?.allowJs;
|
||||
const hasAllowJs =
|
||||
allowJSOption === true || (tsConfigFileName === 'jsconfig.json' && allowJSOption !== false);
|
||||
if (hasAllowJs) return;
|
||||
|
||||
return { tsConfigFileName, contentConfigFileName };
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ type RawContentEvent = { name: ChokidarEvent; entry: string };
|
|||
type ContentEvent = { name: ChokidarEvent; entry: URL };
|
||||
|
||||
type DataEntryMetadata = Record<string, never>;
|
||||
type ContentEntryMetadata = { slug: string };
|
||||
type ContentEntryMetadata = { slug: string, path: string };
|
||||
type CollectionEntryMap = {
|
||||
[collection: string]:
|
||||
| {
|
||||
|
@ -276,7 +276,7 @@ export async function createContentTypesGenerator({
|
|||
if (!(entryKey in collectionEntryMap[collectionKey].entries)) {
|
||||
collectionEntryMap[collectionKey] = {
|
||||
type: 'content',
|
||||
entries: { ...collectionInfo.entries, [entryKey]: { slug: addedSlug } },
|
||||
entries: { ...collectionInfo.entries, [entryKey]: { slug: addedSlug, path: event.entry.toString() } },
|
||||
};
|
||||
}
|
||||
return { shouldGenerateTypes: true };
|
||||
|
@ -453,7 +453,15 @@ async function writeContentFiles({
|
|||
)}] }`;
|
||||
|
||||
const slugType = JSON.stringify(entryMetadata.slug);
|
||||
contentTypesStr += `${entryKey}: {\n id: ${entryKey};\n slug: ${slugType};\n body: string;\n collection: ${collectionKey};\n data: ${dataType}\n} & ${renderType};\n`;
|
||||
contentTypesStr += [
|
||||
`${entryKey}: {`,
|
||||
` id: ${entryKey};`,
|
||||
` slug: ${slugType};`,
|
||||
` path: ${JSON.stringify(entryMetadata.path)};`,
|
||||
` body: string;`,
|
||||
` collection: ${collectionKey};`,
|
||||
` data: ${dataType}`,
|
||||
`} & ${renderType};`].join("\n");
|
||||
}
|
||||
contentTypesStr += `};\n`;
|
||||
break;
|
||||
|
|
|
@ -59,7 +59,7 @@ export default async function build(
|
|||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'build');
|
||||
telemetry.record(eventCliSession('build', userConfig));
|
||||
|
||||
const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const builder = new AstroBuilder(settings, {
|
||||
...options,
|
||||
|
|
|
@ -102,24 +102,18 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
|
|||
};
|
||||
}
|
||||
|
||||
export async function createSettings(config: AstroConfig, cwd?: string): Promise<AstroSettings> {
|
||||
const tsconfig = await loadTSConfig(cwd);
|
||||
export function createSettings(config: AstroConfig, cwd?: string): AstroSettings {
|
||||
const tsconfig = loadTSConfig(cwd);
|
||||
const settings = createBaseSettings(config);
|
||||
|
||||
let watchFiles = [];
|
||||
const watchFiles = tsconfig?.exists ? [tsconfig.path, ...tsconfig.extendedPaths] : [];
|
||||
|
||||
if (cwd) {
|
||||
watchFiles.push(fileURLToPath(new URL('./package.json', pathToFileURL(cwd))));
|
||||
}
|
||||
|
||||
if (typeof tsconfig !== 'string') {
|
||||
watchFiles.push(
|
||||
...[tsconfig.tsconfigFile, ...(tsconfig.extended ?? []).map((e) => e.tsconfigFile)]
|
||||
);
|
||||
settings.tsConfig = tsconfig.tsconfig;
|
||||
settings.tsConfigPath = tsconfig.tsconfigFile;
|
||||
}
|
||||
|
||||
settings.tsConfig = tsconfig?.config;
|
||||
settings.tsConfigPath = tsconfig?.path;
|
||||
settings.watchFiles = watchFiles;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import {
|
||||
TSConfckParseError,
|
||||
find,
|
||||
parse,
|
||||
type TSConfckParseOptions,
|
||||
type TSConfckParseResult,
|
||||
} from 'tsconfck';
|
||||
import type { CompilerOptions, TypeAcquisition } from 'typescript';
|
||||
import * as tsr from 'tsconfig-resolver';
|
||||
|
||||
export const defaultTSConfig: TSConfig = { extends: 'astro/tsconfigs/base' };
|
||||
export const defaultTSConfig: tsr.TsConfigJson = { extends: 'astro/tsconfigs/base' };
|
||||
|
||||
export type frameworkWithTSSettings = 'vue' | 'react' | 'preact' | 'solid-js';
|
||||
// The following presets unfortunately cannot be inside the specific integrations, as we need
|
||||
// them even in cases where the integrations are not installed
|
||||
export const presets = new Map<frameworkWithTSSettings, TSConfig>([
|
||||
export const presets = new Map<frameworkWithTSSettings, tsr.TsConfigJson>([
|
||||
[
|
||||
'vue', // Settings needed for template intellisense when using Volar
|
||||
{
|
||||
|
@ -51,78 +45,52 @@ export const presets = new Map<frameworkWithTSSettings, TSConfig>([
|
|||
],
|
||||
]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
type TSConfigResult<T = {}> = Promise<
|
||||
(TSConfckParseResult & T) | 'invalid-config' | 'missing-config' | 'unknown-error'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Load a tsconfig.json or jsconfig.json is the former is not found
|
||||
* @param root The root directory to search in, defaults to `process.cwd()`.
|
||||
* @param findUp Whether to search for the config file in parent directories, by default only the root directory is searched.
|
||||
* @param cwd Directory to start from
|
||||
* @param resolve Determine if the function should go up directories like TypeScript would
|
||||
*/
|
||||
export async function loadTSConfig(
|
||||
root: string | undefined,
|
||||
findUp = false
|
||||
): Promise<TSConfigResult<{ rawConfig: TSConfckParseResult }>> {
|
||||
const safeCwd = root ?? process.cwd();
|
||||
export function loadTSConfig(cwd: string | undefined, resolve = true): tsr.TsConfigResult {
|
||||
cwd = cwd ?? process.cwd();
|
||||
let config = tsr.tsconfigResolverSync({
|
||||
cwd,
|
||||
filePath: resolve ? undefined : cwd,
|
||||
ignoreExtends: !resolve,
|
||||
});
|
||||
|
||||
const [jsconfig, tsconfig] = await Promise.all(
|
||||
['jsconfig.json', 'tsconfig.json'].map((configName) =>
|
||||
// `tsconfck` expects its first argument to be a file path, not a directory path, so we'll fake one
|
||||
find(join(safeCwd, './dummy.txt'), {
|
||||
root: findUp ? undefined : root,
|
||||
configName: configName,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// If we have both files, prefer tsconfig.json
|
||||
if (tsconfig) {
|
||||
const parsedConfig = await safeParse(tsconfig, { root: root });
|
||||
|
||||
if (typeof parsedConfig === 'string') {
|
||||
return parsedConfig;
|
||||
}
|
||||
|
||||
return { ...parsedConfig, rawConfig: parsedConfig.extended?.[0] ?? parsedConfig.tsconfig };
|
||||
// When a direct filepath is provided to `tsconfigResolver`, it'll instead return invalid-config even when
|
||||
// the file does not exists. We'll manually handle this so we can provide better errors to users
|
||||
if (!resolve && config.reason === 'invalid-config' && !existsSync(join(cwd, 'tsconfig.json'))) {
|
||||
config = { reason: 'not-found', path: undefined, exists: false };
|
||||
}
|
||||
|
||||
if (jsconfig) {
|
||||
const parsedConfig = await safeParse(jsconfig, { root: root });
|
||||
// If we couldn't find a tsconfig.json, try to load a jsconfig.json instead
|
||||
if (config.reason === 'not-found') {
|
||||
const jsconfig = tsr.tsconfigResolverSync({
|
||||
cwd,
|
||||
filePath: resolve ? undefined : cwd,
|
||||
searchName: 'jsconfig.json',
|
||||
ignoreExtends: !resolve,
|
||||
});
|
||||
|
||||
if (typeof parsedConfig === 'string') {
|
||||
return parsedConfig;
|
||||
if (
|
||||
!resolve &&
|
||||
jsconfig.reason === 'invalid-config' &&
|
||||
!existsSync(join(cwd, 'jsconfig.json'))
|
||||
) {
|
||||
return { reason: 'not-found', path: undefined, exists: false };
|
||||
}
|
||||
|
||||
return { ...parsedConfig, rawConfig: parsedConfig.extended?.[0] ?? parsedConfig.tsconfig };
|
||||
return jsconfig;
|
||||
}
|
||||
|
||||
return 'missing-config';
|
||||
}
|
||||
|
||||
async function safeParse(tsconfigPath: string, options: TSConfckParseOptions = {}): TSConfigResult {
|
||||
try {
|
||||
const parseResult = await parse(tsconfigPath, options);
|
||||
|
||||
if (parseResult.tsconfig == null) {
|
||||
return 'missing-config';
|
||||
}
|
||||
|
||||
return parseResult;
|
||||
} catch (e) {
|
||||
if (e instanceof TSConfckParseError) {
|
||||
return 'invalid-config';
|
||||
}
|
||||
|
||||
return 'unknown-error';
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function updateTSConfigForFramework(
|
||||
target: TSConfig,
|
||||
target: tsr.TsConfigJson,
|
||||
framework: frameworkWithTSSettings
|
||||
): TSConfig {
|
||||
): tsr.TsConfigJson {
|
||||
if (!presets.has(framework)) {
|
||||
return target;
|
||||
}
|
||||
|
@ -152,32 +120,3 @@ function deepMergeObjects<T extends Record<string, any>>(a: T, b: T): T {
|
|||
|
||||
return merged;
|
||||
}
|
||||
|
||||
// The code below is adapted from `pkg-types`
|
||||
// `pkg-types` offer more types and utilities, but since we only want the TSConfig type, we'd rather avoid adding a dependency.
|
||||
// https://github.com/unjs/pkg-types/blob/78328837d369d0145a8ddb35d7fe1fadda4bfadf/src/types/tsconfig.ts
|
||||
// See https://github.com/unjs/pkg-types/blob/78328837d369d0145a8ddb35d7fe1fadda4bfadf/LICENSE for license information
|
||||
|
||||
export type StripEnums<T extends Record<string, any>> = {
|
||||
[K in keyof T]: T[K] extends boolean
|
||||
? T[K]
|
||||
: T[K] extends string
|
||||
? T[K]
|
||||
: T[K] extends object
|
||||
? T[K]
|
||||
: T[K] extends Array<any>
|
||||
? T[K]
|
||||
: T[K] extends undefined
|
||||
? undefined
|
||||
: any;
|
||||
};
|
||||
|
||||
export interface TSConfig {
|
||||
compilerOptions?: StripEnums<CompilerOptions>;
|
||||
compileOnSave?: boolean;
|
||||
extends?: string;
|
||||
files?: string[];
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ export async function restartContainer(container: Container): Promise<Container
|
|||
|
||||
try {
|
||||
const { astroConfig } = await resolveConfig(container.inlineConfig, 'dev', container.fs);
|
||||
const settings = await createSettings(astroConfig, fileURLToPath(existingSettings.config.root));
|
||||
const settings = createSettings(astroConfig, fileURLToPath(existingSettings.config.root));
|
||||
await close();
|
||||
return await createRestartedContainer(container, settings);
|
||||
} catch (_err) {
|
||||
|
@ -105,7 +105,7 @@ export async function createContainerWithAutomaticRestart({
|
|||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'dev', fs);
|
||||
telemetry.record(eventCliSession('dev', userConfig));
|
||||
|
||||
const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const initialContainer = await createContainer({ settings, logger: logger, inlineConfig, fs });
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise<
|
|||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'preview');
|
||||
telemetry.record(eventCliSession('preview', userConfig));
|
||||
|
||||
const _settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
const _settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const settings = await runHookConfigSetup({
|
||||
settings: _settings,
|
||||
|
|
|
@ -45,7 +45,7 @@ export default async function sync(
|
|||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'sync');
|
||||
telemetry.record(eventCliSession('sync', userConfig));
|
||||
|
||||
const _settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
const _settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const settings = await runHookConfigSetup({
|
||||
settings: _settings,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import path from 'node:path';
|
||||
import type { CompilerOptions } from 'typescript';
|
||||
import { normalizePath, type ResolvedConfig, type Plugin as VitePlugin } from 'vite';
|
||||
import type { AstroSettings } from '../@types/astro.js';
|
||||
|
||||
|
@ -13,7 +12,7 @@ const getConfigAlias = (settings: AstroSettings): Alias[] | null => {
|
|||
const { tsConfig, tsConfigPath } = settings;
|
||||
if (!tsConfig || !tsConfigPath || !tsConfig.compilerOptions) return null;
|
||||
|
||||
const { baseUrl, paths } = tsConfig.compilerOptions as CompilerOptions;
|
||||
const { baseUrl, paths } = tsConfig.compilerOptions;
|
||||
if (!baseUrl) return null;
|
||||
|
||||
// resolve the base url from the configuration file directory
|
||||
|
|
|
@ -1,57 +1,68 @@
|
|||
import { expect } from 'chai';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { loadTSConfig, updateTSConfigForFramework } from '../../../dist/core/config/index.js';
|
||||
import * as path from 'node:path';
|
||||
import * as tsr from 'tsconfig-resolver';
|
||||
|
||||
const cwd = fileURLToPath(new URL('../../fixtures/tsconfig-handling/', import.meta.url));
|
||||
|
||||
describe('TSConfig handling', () => {
|
||||
beforeEach(() => {
|
||||
// `tsconfig-resolver` has a weird internal cache that only vaguely respect its own rules when not resolving
|
||||
// so we need to clear it before each test or we'll get false positives. This should only be relevant in tests.
|
||||
tsr.clearCache();
|
||||
});
|
||||
|
||||
describe('tsconfig / jsconfig loading', () => {
|
||||
it('can load tsconfig.json', async () => {
|
||||
const config = await loadTSConfig(cwd);
|
||||
it('can load tsconfig.json', () => {
|
||||
const config = loadTSConfig(cwd);
|
||||
|
||||
expect(config).to.not.be.undefined;
|
||||
expect(config.exists).to.equal(true);
|
||||
expect(config.config.files).to.deep.equal(['im-a-test']);
|
||||
});
|
||||
|
||||
it('can resolve tsconfig.json up directories', async () => {
|
||||
const config = await loadTSConfig(cwd);
|
||||
it('can resolve tsconfig.json up directories', () => {
|
||||
const config = loadTSConfig(path.join(cwd, 'nested-folder'));
|
||||
|
||||
expect(config).to.not.be.undefined;
|
||||
expect(config.tsconfigFile).to.equal(path.join(cwd, 'tsconfig.json'));
|
||||
expect(config.tsconfig.files).to.deep.equal(['im-a-test']);
|
||||
expect(config.exists).to.equal(true);
|
||||
expect(config.path).to.equal(path.join(cwd, 'tsconfig.json'));
|
||||
expect(config.config.files).to.deep.equal(['im-a-test']);
|
||||
});
|
||||
|
||||
it('can fallback to jsconfig.json if tsconfig.json does not exists', async () => {
|
||||
const config = await loadTSConfig(path.join(cwd, 'jsconfig'));
|
||||
it('can fallback to jsconfig.json if tsconfig.json does not exists', () => {
|
||||
const config = loadTSConfig(path.join(cwd, 'jsconfig'), false);
|
||||
|
||||
expect(config).to.not.be.undefined;
|
||||
expect(config.tsconfigFile).to.equal(path.join(cwd, 'jsconfig', 'jsconfig.json'));
|
||||
expect(config.tsconfig.files).to.deep.equal(['im-a-test-js']);
|
||||
expect(config.exists).to.equal(true);
|
||||
expect(config.path).to.equal(path.join(cwd, 'jsconfig', 'jsconfig.json'));
|
||||
expect(config.config.files).to.deep.equal(['im-a-test-js']);
|
||||
});
|
||||
|
||||
it('properly return errors when not resolving', async () => {
|
||||
const invalidConfig = await loadTSConfig(path.join(cwd, 'invalid'));
|
||||
const missingConfig = await loadTSConfig(path.join(cwd, 'missing'));
|
||||
it('properly return errors when not resolving', () => {
|
||||
const invalidConfig = loadTSConfig(path.join(cwd, 'invalid'), false);
|
||||
const missingConfig = loadTSConfig(path.join(cwd, 'missing'), false);
|
||||
|
||||
expect(invalidConfig).to.equal('invalid-config');
|
||||
expect(missingConfig).to.equal('missing-config');
|
||||
expect(invalidConfig.exists).to.equal(false);
|
||||
expect(invalidConfig.reason).to.equal('invalid-config');
|
||||
|
||||
expect(missingConfig.exists).to.equal(false);
|
||||
expect(missingConfig.reason).to.equal('not-found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tsconfig / jsconfig updates', () => {
|
||||
it('can update a tsconfig with a framework config', async () => {
|
||||
const config = await loadTSConfig(cwd);
|
||||
const updatedConfig = updateTSConfigForFramework(config.tsconfig, 'react');
|
||||
it('can update a tsconfig with a framework config', () => {
|
||||
const config = loadTSConfig(cwd);
|
||||
const updatedConfig = updateTSConfigForFramework(config.config, 'react');
|
||||
|
||||
expect(config.tsconfig).to.not.equal('react-jsx');
|
||||
expect(config.config).to.not.equal('react-jsx');
|
||||
expect(updatedConfig.compilerOptions.jsx).to.equal('react-jsx');
|
||||
});
|
||||
|
||||
it('produce no changes on invalid frameworks', async () => {
|
||||
const config = await loadTSConfig(cwd);
|
||||
const updatedConfig = updateTSConfigForFramework(config.tsconfig, 'doesnt-exist');
|
||||
it('produce no changes on invalid frameworks', () => {
|
||||
const config = loadTSConfig(cwd);
|
||||
const updatedConfig = updateTSConfigForFramework(config.config, 'doesnt-exist');
|
||||
|
||||
expect(config.tsconfig).to.deep.equal(updatedConfig);
|
||||
expect(config.config).to.deep.equal(updatedConfig);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -116,14 +116,14 @@ export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroI
|
|||
if (!id.endsWith('.mdx')) return;
|
||||
|
||||
// Read code from file manually to prevent Vite from parsing `import.meta.env` expressions
|
||||
const { fileId } = getFileInfo(id, config);
|
||||
const { fileId, fileUrl } = getFileInfo(id, config);
|
||||
const code = await fs.readFile(fileId, 'utf-8');
|
||||
|
||||
const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
|
||||
|
||||
const vfile = new VFile({ value: pageContent, path: id });
|
||||
// Ensure `data.astro` is available to all remark plugins
|
||||
setVfileFrontmatter(vfile, frontmatter);
|
||||
setVfileFrontmatter(vfile, frontmatter, { fileURL: new URL(fileUrl) });
|
||||
|
||||
try {
|
||||
const compiled = await processor.process(vfile);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { VFileData as Data, VFile } from 'vfile';
|
||||
import type { MarkdownAstroData } from './types.js';
|
||||
import type { MarkdownAstroData, MarkdownProcessorRenderOptions } from './types.js';
|
||||
|
||||
function isValidAstroData(obj: unknown): obj is MarkdownAstroData {
|
||||
if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) {
|
||||
|
@ -27,10 +27,15 @@ export function safelyGetAstroData(vfileData: Data): MarkdownAstroData | Invalid
|
|||
return astro;
|
||||
}
|
||||
|
||||
export function setVfileFrontmatter(vfile: VFile, frontmatter: Record<string, any>) {
|
||||
export function setVfileFrontmatter(
|
||||
vfile: VFile,
|
||||
frontmatter: Record<string, any>,
|
||||
renderOpts: MarkdownProcessorRenderOptions | undefined
|
||||
) {
|
||||
vfile.data ??= {};
|
||||
vfile.data.astro ??= {};
|
||||
(vfile.data.astro as any).frontmatter = frontmatter;
|
||||
(vfile.data.astro as any).fileURL = renderOpts?.fileURL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,8 +124,9 @@ export async function createMarkdownProcessor(
|
|||
|
||||
return {
|
||||
async render(content, renderOpts) {
|
||||
console.log('url', renderOpts?.fileURL);
|
||||
const vfile = new VFile({ value: content, path: renderOpts?.fileURL });
|
||||
setVfileFrontmatter(vfile, renderOpts?.frontmatter ?? {});
|
||||
setVfileFrontmatter(vfile, renderOpts?.frontmatter ?? {}, renderOpts);
|
||||
|
||||
const result: MarkdownVFile = await parser.process(vfile).catch((err) => {
|
||||
// Ensure that the error message contains the input filename
|
||||
|
|
|
@ -625,9 +625,9 @@ importers:
|
|||
strip-ansi:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
tsconfck:
|
||||
specifier: 3.0.0-next.9
|
||||
version: 3.0.0-next.9(typescript@5.1.6)
|
||||
tsconfig-resolver:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
unist-util-visit:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2
|
||||
|
@ -8834,7 +8834,6 @@ packages:
|
|||
|
||||
/@types/json5@0.0.30:
|
||||
resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==}
|
||||
dev: true
|
||||
|
||||
/@types/katex@0.16.0:
|
||||
resolution: {integrity: sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==}
|
||||
|
@ -8984,7 +8983,6 @@ packages:
|
|||
|
||||
/@types/resolve@1.20.2:
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
dev: true
|
||||
|
||||
/@types/sax@1.2.4:
|
||||
resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==}
|
||||
|
@ -16597,7 +16595,6 @@ packages:
|
|||
/strip-bom@4.0.0:
|
||||
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/strip-comments@2.0.1:
|
||||
resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
|
||||
|
@ -17020,19 +17017,6 @@ packages:
|
|||
code-block-writer: 12.0.0
|
||||
dev: true
|
||||
|
||||
/tsconfck@3.0.0-next.9(typescript@5.1.6):
|
||||
resolution: {integrity: sha512-bgVlu3qcRUZpm9Au1IHiPDkb8XU+72bRkXrBaJsiAjIlixtkbKLe4q1odrrqG0rVHvh0Q4R3adT/nh1FwzftXA==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
typescript: 5.1.6
|
||||
dev: false
|
||||
|
||||
/tsconfig-resolver@3.0.1:
|
||||
resolution: {integrity: sha512-ZHqlstlQF449v8glscGRXzL6l2dZvASPCdXJRWG4gHEZlUVx2Jtmr+a2zeVG4LCsKhDXKRj5R3h0C/98UcVAQg==}
|
||||
dependencies:
|
||||
|
@ -17042,7 +17026,6 @@ packages:
|
|||
resolve: 1.22.4
|
||||
strip-bom: 4.0.0
|
||||
type-fest: 3.0.0
|
||||
dev: true
|
||||
|
||||
/tsconfig@7.0.0:
|
||||
resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==}
|
||||
|
@ -17193,7 +17176,6 @@ packages:
|
|||
/type-fest@3.0.0:
|
||||
resolution: {integrity: sha512-MINvUN5ug9u+0hJDzSZNSnuKXI8M4F5Yvb6SQZ2CYqe7SgKXKOosEcU5R7tRgo85I6eAVBbkVF7TCvB4AUK2xQ==}
|
||||
engines: {node: '>=14.16'}
|
||||
dev: true
|
||||
|
||||
/type-is@1.6.18:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
|
|
Loading…
Reference in a new issue