From 832428945331e7ca3649fd3934726de6a10eff32 Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Tue, 10 Oct 2023 18:51:34 +0200 Subject: [PATCH 1/8] feat: use tsconfck instead of tsconfig-resolver --- packages/astro/package.json | 2 +- packages/astro/src/@types/astro.ts | 4 +- packages/astro/src/cli/add/index.ts | 26 ++-- packages/astro/src/config/index.ts | 2 +- .../astro/src/content/server-listeners.ts | 16 +-- packages/astro/src/core/build/index.ts | 2 +- packages/astro/src/core/config/settings.ts | 18 ++- packages/astro/src/core/config/tsconfig.ts | 127 +++++++++++++----- packages/astro/src/core/dev/restart.ts | 4 +- packages/astro/src/core/preview/index.ts | 2 +- packages/astro/src/core/sync/index.ts | 2 +- .../src/vite-plugin-config-alias/index.ts | 3 +- .../test/units/config/config-tsconfig.test.js | 49 +++---- pnpm-lock.yaml | 24 +++- 14 files changed, 178 insertions(+), 103 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 33bf6eb4fd1c..936ac2c3676c 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -167,7 +167,7 @@ "shiki": "^0.14.3", "string-width": "^6.1.0", "strip-ansi": "^7.1.0", - "tsconfig-resolver": "^3.0.1", + "tsconfck": "3.0.0-next.9", "unist-util-visit": "^4.1.2", "vfile": "^5.3.7", "vite": "^4.4.9", diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 2217e76f2bb7..45a82b355e77 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -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; - tsConfig: TsConfigJson | undefined; + tsConfig: TSConfig | undefined; tsConfigPath: string | undefined; watchFiles: string[]; timer: AstroTimer; diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index 859a95c0a93b..d47db0e9907d 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -849,25 +849,31 @@ async function updateTSConfig( return UpdateResult.none; } - const inputConfig = loadTSConfig(cwd, false); - const configFileName = inputConfig.exists ? inputConfig.path.split('/').pop() : 'tsconfig.json'; + let inputConfig = await loadTSConfig(cwd); + let inputConfigText = ''; - if (inputConfig.reason === 'invalid-config') { + if (inputConfig === 'invalid-config') { return UpdateResult.failure; - } - - if (inputConfig.reason === 'not-found') { + } else if (inputConfig === 'missing-config' || inputConfig === 'unknown-error') { 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(); + const outputConfig = updateTSConfigForFramework( - inputConfig.exists ? inputConfig.config : defaultTSConfig, + inputConfig.rawConfig.tsconfig, firstIntegrationWithTSSettings ); - const input = inputConfig.exists ? JSON.stringify(inputConfig.config, null, 2) : ''; const output = JSON.stringify(outputConfig, null, 2); - const diff = getDiffContent(input, output); + const diff = getDiffContent(inputConfigText, output); if (!diff) { return UpdateResult.none; @@ -907,7 +913,7 @@ async function updateTSConfig( } if (await askToContinue({ flags })) { - await fs.writeFile(inputConfig?.path ?? path.join(cwd, 'tsconfig.json'), output, { + await fs.writeFile(inputConfig.tsconfigFile, output, { encoding: 'utf-8', }); logger.debug('add', `Updated ${configFileName} file`); diff --git a/packages/astro/src/config/index.ts b/packages/astro/src/config/index.ts index 8625140ea5e4..fc45ae983476 100644 --- a/packages/astro/src/config/index.ts +++ b/packages/astro/src/config/index.ts @@ -35,7 +35,7 @@ export function getViteConfig(inlineConfig: UserConfig) { level: 'info', }); const { astroConfig: config } = await resolveConfig({}, cmd); - const settings = createSettings(config, inlineConfig.root); + const settings = await createSettings(config, inlineConfig.root); await runHookConfigSetup({ settings, command: cmd, logger }); const viteConfig = await createVite( { diff --git a/packages/astro/src/content/server-listeners.ts b/packages/astro/src/content/server-listeners.ts index c5e3da2c4252..699d5f2710dd 100644 --- a/packages/astro/src/content/server-listeners.ts +++ b/packages/astro/src/content/server-listeners.ts @@ -32,7 +32,7 @@ export async function attachContentServerListeners({ contentPaths.contentDir.href.replace(settings.config.root.href, '') )} for changes` ); - const maybeTsConfigStats = getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings }); + const maybeTsConfigStats = await 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 ); } -function getTSConfigStatsWhenAllowJsFalse({ +async function getTSConfigStatsWhenAllowJsFalse({ contentPaths, settings, }: { @@ -108,15 +108,15 @@ function getTSConfigStatsWhenAllowJsFalse({ ); if (!isContentConfigJsFile) return; - const inputConfig = loadTSConfig(fileURLToPath(settings.config.root), false); - const tsConfigFileName = inputConfig.exists && inputConfig.path.split(path.sep).pop(); + const inputConfig = await loadTSConfig(fileURLToPath(settings.config.root)); + if (typeof inputConfig === 'string') return; + + const tsConfigFileName = inputConfig.tsconfigFile.split(path.sep).pop(); if (!tsConfigFileName) return; const contentConfigFileName = contentPaths.config.url.pathname.split(path.sep).pop()!; - const allowJSOption = inputConfig?.config?.compilerOptions?.allowJs; - const hasAllowJs = - allowJSOption === true || (tsConfigFileName === 'jsconfig.json' && allowJSOption !== false); - if (hasAllowJs) return; + const allowJSOption = inputConfig.tsconfig.compilerOptions?.allowJs; + if (allowJSOption) return; return { tsConfigFileName, contentConfigFileName }; } diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 5f5ae69a1f78..797a29a2589e 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -59,7 +59,7 @@ export default async function build( const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'build'); telemetry.record(eventCliSession('build', userConfig)); - const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root)); + const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root)); const builder = new AstroBuilder(settings, { ...options, diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index 8b70f6e96017..2c32de2b448c 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -102,18 +102,22 @@ export function createBaseSettings(config: AstroConfig): AstroSettings { }; } -export function createSettings(config: AstroConfig, cwd?: string): AstroSettings { - const tsconfig = loadTSConfig(cwd); +export async function createSettings(config: AstroConfig, cwd?: string): Promise { + const tsconfig = await loadTSConfig(cwd); const settings = createBaseSettings(config); - const watchFiles = tsconfig?.exists ? [tsconfig.path, ...tsconfig.extendedPaths] : []; - + let watchFiles = []; if (cwd) { watchFiles.push(fileURLToPath(new URL('./package.json', pathToFileURL(cwd)))); } - settings.tsConfig = tsconfig?.config; - settings.tsConfigPath = tsconfig?.path; - settings.watchFiles = watchFiles; + if (typeof tsconfig !== 'string') { + watchFiles = [tsconfig.tsconfigFile, ...(tsconfig.extended ?? []).map((e) => e.tsconfigFile)]; + + settings.watchFiles = watchFiles; + settings.tsConfig = tsconfig.tsconfig; + settings.tsConfigPath = tsconfig.tsconfigFile; + } + return settings; } diff --git a/packages/astro/src/core/config/tsconfig.ts b/packages/astro/src/core/config/tsconfig.ts index a0c78f08c62e..408c2a1a4442 100644 --- a/packages/astro/src/core/config/tsconfig.ts +++ b/packages/astro/src/core/config/tsconfig.ts @@ -1,13 +1,19 @@ -import { existsSync } from 'node:fs'; import { join } from 'node:path'; -import * as tsr from 'tsconfig-resolver'; +import { + TSConfckParseError, + find, + parse, + type TSConfckParseOptions, + type TSConfckParseResult, +} from 'tsconfck'; +import type { CompilerOptions, TypeAcquisition } from 'typescript'; -export const defaultTSConfig: tsr.TsConfigJson = { extends: 'astro/tsconfigs/base' }; +export const defaultTSConfig: TSConfig = { 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([ +export const presets = new Map([ [ 'vue', // Settings needed for template intellisense when using Volar { @@ -45,52 +51,74 @@ export const presets = new Map([ ], ]); +// eslint-disable-next-line @typescript-eslint/ban-types +type TSConfigResult = Promise< + (TSConfckParseResult & T) | 'invalid-config' | 'missing-config' | 'unknown-error' +>; + /** * Load a tsconfig.json or jsconfig.json is the former is not found * @param cwd Directory to start from * @param resolve Determine if the function should go up directories like TypeScript would */ -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, - }); - - // 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 }; +export async function loadTSConfig( + cwd: string | undefined +): Promise> { + const safeCwd = cwd ?? process.cwd(); + + 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 fake one + find(join(safeCwd, './dummy.txt'), { root: cwd, configName: configName }) + ) + ); + + // If we have both files, prefer tsconfig.json + if (tsconfig) { + const parsedConfig = await safeParse(tsconfig, { root: cwd }); + + if (typeof parsedConfig === 'string') { + return parsedConfig; + } + + return { ...parsedConfig, rawConfig: parsedConfig.extended?.[0] ?? parsedConfig.tsconfig }; } - // 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 ( - !resolve && - jsconfig.reason === 'invalid-config' && - !existsSync(join(cwd, 'jsconfig.json')) - ) { - return { reason: 'not-found', path: undefined, exists: false }; + if (jsconfig) { + const parsedConfig = await safeParse(jsconfig, { root: cwd }); + + if (typeof parsedConfig === 'string') { + return parsedConfig; } - return jsconfig; + return { ...parsedConfig, rawConfig: parsedConfig.extended?.[0] ?? parsedConfig.tsconfig }; } - return config; + 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'; + } } export function updateTSConfigForFramework( - target: tsr.TsConfigJson, + target: TSConfig, framework: frameworkWithTSSettings -): tsr.TsConfigJson { +): TSConfig { if (!presets.has(framework)) { return target; } @@ -120,3 +148,32 @@ function deepMergeObjects>(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> = { + [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 + ? T[K] + : T[K] extends undefined + ? undefined + : any; +}; + +export interface TSConfig { + compilerOptions?: StripEnums; + compileOnSave?: boolean; + extends?: string; + files?: string[]; + include?: string[]; + exclude?: string[]; + typeAcquisition?: TypeAcquisition; +} diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index 2d6ba75f31ff..f03db39cf957 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -64,7 +64,7 @@ export async function restartContainer(container: Container): Promise { const { tsConfig, tsConfigPath } = settings; if (!tsConfig || !tsConfigPath || !tsConfig.compilerOptions) return null; - const { baseUrl, paths } = tsConfig.compilerOptions; + const { baseUrl, paths } = tsConfig.compilerOptions as CompilerOptions; if (!baseUrl) return null; // resolve the base url from the configuration file directory diff --git a/packages/astro/test/units/config/config-tsconfig.test.js b/packages/astro/test/units/config/config-tsconfig.test.js index 4b87793a949b..09556d0835be 100644 --- a/packages/astro/test/units/config/config-tsconfig.test.js +++ b/packages/astro/test/units/config/config-tsconfig.test.js @@ -1,51 +1,40 @@ 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', () => { - const config = loadTSConfig(cwd); + it('can load tsconfig.json', async () => { + const config = await loadTSConfig(cwd); - expect(config.exists).to.equal(true); - expect(config.config.files).to.deep.equal(['im-a-test']); + expect(config).to.not.be.undefined; }); - it('can resolve tsconfig.json up directories', () => { - const config = loadTSConfig(path.join(cwd, 'nested-folder')); + it('can resolve tsconfig.json up directories', async () => { + const config = await loadTSConfig(path.join(cwd, 'nested-folder')); - 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']); + 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']); }); - it('can fallback to jsconfig.json if tsconfig.json does not exists', () => { - const config = loadTSConfig(path.join(cwd, 'jsconfig'), false); + it('can fallback to jsconfig.json if tsconfig.json does not exists', async () => { + const config = await loadTSConfig(path.join(cwd, 'jsconfig')); - 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']); + 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']); }); - 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.exists).to.equal(false); - expect(invalidConfig.reason).to.equal('invalid-config'); + it('properly return errors when not resolving', async () => { + const invalidConfig = await loadTSConfig(path.join(cwd, 'invalid')); + const missingConfig = await loadTSConfig(path.join(cwd, 'missing')); - expect(missingConfig.exists).to.equal(false); - expect(missingConfig.reason).to.equal('not-found'); + expect(invalidConfig).to.equal('invalid-config'); + expect(missingConfig).to.equal('missing-config'); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c398755668fa..9a49bbf35ece 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -625,9 +625,9 @@ importers: strip-ansi: specifier: ^7.1.0 version: 7.1.0 - tsconfig-resolver: - specifier: ^3.0.1 - version: 3.0.1 + tsconfck: + specifier: 3.0.0-next.9 + version: 3.0.0-next.9(typescript@5.1.6) unist-util-visit: specifier: ^4.1.2 version: 4.1.2 @@ -8834,6 +8834,7 @@ 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==} @@ -8983,6 +8984,7 @@ 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==} @@ -16595,6 +16597,7 @@ 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==} @@ -17017,6 +17020,19 @@ 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: @@ -17026,6 +17042,7 @@ 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==} @@ -17176,6 +17193,7 @@ 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==} From 12fc88f67effca60b28ebcce8db563d84c51abcf Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Tue, 10 Oct 2023 18:54:51 +0200 Subject: [PATCH 2/8] chore: changeset --- .changeset/giant-dolphins-mix.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/giant-dolphins-mix.md diff --git a/.changeset/giant-dolphins-mix.md b/.changeset/giant-dolphins-mix.md new file mode 100644 index 000000000000..a68fa35fb2e0 --- /dev/null +++ b/.changeset/giant-dolphins-mix.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixed `tsconfig.json`'s new array format for `extends` not working From 50f00f480fb822c10f7e1b3b59c3b5a8b15208b0 Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Tue, 10 Oct 2023 19:24:58 +0200 Subject: [PATCH 3/8] test: fix --- packages/astro/src/core/config/tsconfig.ts | 20 +++++++++++-------- .../test/units/config/config-tsconfig.test.js | 18 ++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/astro/src/core/config/tsconfig.ts b/packages/astro/src/core/config/tsconfig.ts index 408c2a1a4442..debb0a3ff19a 100644 --- a/packages/astro/src/core/config/tsconfig.ts +++ b/packages/astro/src/core/config/tsconfig.ts @@ -58,24 +58,28 @@ type TSConfigResult = Promise< /** * Load a tsconfig.json or jsconfig.json is the former is not found - * @param cwd Directory to start from - * @param resolve Determine if the function should go up directories like TypeScript would + * @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. */ export async function loadTSConfig( - cwd: string | undefined + root: string | undefined, + findUp = false ): Promise> { - const safeCwd = cwd ?? process.cwd(); + const safeCwd = root ?? process.cwd(); 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 fake one - find(join(safeCwd, './dummy.txt'), { root: cwd, configName: 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 ? root : undefined, + configName: configName, + }) ) ); // If we have both files, prefer tsconfig.json if (tsconfig) { - const parsedConfig = await safeParse(tsconfig, { root: cwd }); + const parsedConfig = await safeParse(tsconfig, { root: root }); if (typeof parsedConfig === 'string') { return parsedConfig; @@ -85,7 +89,7 @@ export async function loadTSConfig( } if (jsconfig) { - const parsedConfig = await safeParse(jsconfig, { root: cwd }); + const parsedConfig = await safeParse(jsconfig, { root: root }); if (typeof parsedConfig === 'string') { return parsedConfig; diff --git a/packages/astro/test/units/config/config-tsconfig.test.js b/packages/astro/test/units/config/config-tsconfig.test.js index 09556d0835be..8617e35ea61e 100644 --- a/packages/astro/test/units/config/config-tsconfig.test.js +++ b/packages/astro/test/units/config/config-tsconfig.test.js @@ -14,7 +14,7 @@ describe('TSConfig handling', () => { }); it('can resolve tsconfig.json up directories', async () => { - const config = await loadTSConfig(path.join(cwd, 'nested-folder')); + const config = await loadTSConfig(cwd); expect(config).to.not.be.undefined; expect(config.tsconfigFile).to.equal(path.join(cwd, 'tsconfig.json')); @@ -39,19 +39,19 @@ describe('TSConfig handling', () => { }); describe('tsconfig / jsconfig updates', () => { - it('can update a tsconfig with a framework config', () => { - const config = loadTSConfig(cwd); - const updatedConfig = updateTSConfigForFramework(config.config, 'react'); + it('can update a tsconfig with a framework config', async () => { + const config = await loadTSConfig(cwd); + const updatedConfig = updateTSConfigForFramework(config.tsconfig, 'react'); - expect(config.config).to.not.equal('react-jsx'); + expect(config.tsconfig).to.not.equal('react-jsx'); expect(updatedConfig.compilerOptions.jsx).to.equal('react-jsx'); }); - it('produce no changes on invalid frameworks', () => { - const config = loadTSConfig(cwd); - const updatedConfig = updateTSConfigForFramework(config.config, 'doesnt-exist'); + it('produce no changes on invalid frameworks', async () => { + const config = await loadTSConfig(cwd); + const updatedConfig = updateTSConfigForFramework(config.tsconfig, 'doesnt-exist'); - expect(config.config).to.deep.equal(updatedConfig); + expect(config.tsconfig).to.deep.equal(updatedConfig); }); }); }); From 75d947de7fbc138e10b5a135e3edd212ac94c9fe Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Tue, 10 Oct 2023 19:34:18 +0200 Subject: [PATCH 4/8] test: invert logic --- packages/astro/src/core/config/tsconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/config/tsconfig.ts b/packages/astro/src/core/config/tsconfig.ts index debb0a3ff19a..db4b1392ff2a 100644 --- a/packages/astro/src/core/config/tsconfig.ts +++ b/packages/astro/src/core/config/tsconfig.ts @@ -71,7 +71,7 @@ export async function loadTSConfig( ['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 ? root : undefined, + root: findUp ? undefined : root, configName: configName, }) ) From d707b08c48b8e56e8bec8afdff562f962bb10da4 Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Wed, 11 Oct 2023 11:06:50 +0200 Subject: [PATCH 5/8] fix: make sure package.json is in te list of watched files --- packages/astro/src/core/config/settings.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index 2c32de2b448c..07f22a33c080 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -112,12 +112,14 @@ export async function createSettings(config: AstroConfig, cwd?: string): Promise } if (typeof tsconfig !== 'string') { - watchFiles = [tsconfig.tsconfigFile, ...(tsconfig.extended ?? []).map((e) => e.tsconfigFile)]; - - settings.watchFiles = watchFiles; + watchFiles.push( + ...[tsconfig.tsconfigFile, ...(tsconfig.extended ?? []).map((e) => e.tsconfigFile)] + ); settings.tsConfig = tsconfig.tsconfig; settings.tsConfigPath = tsconfig.tsconfigFile; } + settings.watchFiles = watchFiles; + return settings; } From 9a2a202586e2e814deb34d0d2b417cd2310b5a4f Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Wed, 11 Oct 2023 11:18:21 +0200 Subject: [PATCH 6/8] fix: don't do anything on unknown errors --- packages/astro/src/cli/add/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index f84f67f1e763..a20a20e4b7d7 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -851,9 +851,9 @@ async function updateTSConfig( let inputConfig = await loadTSConfig(cwd); let inputConfigText = ''; - if (inputConfig === 'invalid-config') { + if (inputConfig === 'invalid-config' || inputConfig === 'unknown-error') { return UpdateResult.failure; - } else if (inputConfig === 'missing-config' || inputConfig === 'unknown-error') { + } else if (inputConfig === 'missing-config') { logger.debug('add', "Couldn't find tsconfig.json or jsconfig.json, generating one"); inputConfig = { tsconfig: defaultTSConfig, From 8c64d26e234573c55dff6a47acb52d6cdd0074ce Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Wed, 11 Oct 2023 17:26:57 +0200 Subject: [PATCH 7/8] fix: use basename --- packages/astro/src/cli/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts index a20a20e4b7d7..a6c4727ba92e 100644 --- a/packages/astro/src/cli/add/index.ts +++ b/packages/astro/src/cli/add/index.ts @@ -864,7 +864,7 @@ async function updateTSConfig( inputConfigText = JSON.stringify(inputConfig.rawConfig.tsconfig, null, 2); } - const configFileName = inputConfig.tsconfigFile.split('/').pop(); + const configFileName = path.basename(inputConfig.tsconfigFile); const outputConfig = updateTSConfigForFramework( inputConfig.rawConfig.tsconfig, From be5f75448da1eab6c1e41059de4fcc4334929a2b Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Wed, 11 Oct 2023 18:00:27 +0200 Subject: [PATCH 8/8] chore: use minor for changeset --- .changeset/giant-dolphins-mix.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/giant-dolphins-mix.md b/.changeset/giant-dolphins-mix.md index a68fa35fb2e0..8b7d4844093e 100644 --- a/.changeset/giant-dolphins-mix.md +++ b/.changeset/giant-dolphins-mix.md @@ -1,5 +1,5 @@ --- -'astro': patch +'astro': minor --- -Fixed `tsconfig.json`'s new array format for `extends` not working +Fixed `tsconfig.json`'s new array format for `extends` not working. This was done by migrating Astro to use [`tsconfck`](https://github.com/dominikg/tsconfck) instead of [`tsconfig-resolver`](https://github.com/ifiokjr/tsconfig-resolver) to find and parse `tsconfig.json` files.