Skip to content

Commit e69b48f

Browse files
authored
fix(cli-utils, internal): Fix retrieval of tsconfig.json extends and standard TS libs (#251)
1 parent c81d3aa commit e69b48f

File tree

6 files changed

+71
-82
lines changed

6 files changed

+71
-82
lines changed

.changeset/nervous-rats-stare.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@gql.tada/cli-utils": patch
3+
---
4+
5+
Fix resolution of default lib path when libs aren't in standard location

.changeset/violet-tools-relax.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@gql.tada/internal": patch
3+
---
4+
5+
Fix resolving `tsconfig.json`' `extends` option

packages/cli-utils/src/ts/container.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { SourcePosition } from './utils';
99
import { spanToFilePosition } from './utils';
1010

1111
function maybeBind<T extends Function>(that: object, fn: T | undefined): T {
12-
return fn ? fn.bind(that, fn) : fn;
12+
return fn ? fn.bind(that) : fn;
1313
}
1414

1515
export interface PluginCreateInfo<Config extends {} = GraphQLSPConfig>

packages/cli-utils/src/ts/factory.ts

+22-41
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,14 @@ export const programFactory = (params: ProgramFactoryParams): ProgramFactory =>
4949
const virtualMap: VirtualMap = new Map();
5050

5151
const projectRoot = path.dirname(params.configPath);
52-
const tslibPath = path.join(projectRoot, 'node_modules/typescript/lib/');
53-
const system = createFSBackedSystem(vfsMap, projectRoot, ts, tslibPath);
52+
const system = createFSBackedSystem(vfsMap, projectRoot, ts, resolveDefaultLibsPath(params));
5453
const config = resolveConfig(params, system);
5554

56-
for (const { filename, contents } of resolveLibs(params)) {
57-
if (contents) system.writeFile(path.join(tslibPath, filename), contents);
58-
}
59-
6055
const rootNames = new Set(config.fileNames);
6156
const options = {
57+
getDefaultLibFilePath: ts.getDefaultLibFilePath(config.options),
6258
...defaultCompilerOptions,
6359
...config.options,
64-
getDefaultLibFilePath: tslibPath,
6560
};
6661
const host = createVirtualCompilerHost(system, options, ts);
6762

@@ -178,45 +173,31 @@ export const programFactory = (params: ProgramFactoryParams): ProgramFactory =>
178173
return factory;
179174
};
180175

181-
interface LibFile {
182-
filename: string;
183-
contents: string;
184-
}
185-
186176
const defaultCompilerOptions = {
187177
target: ts.ScriptTarget.Latest,
188178
} satisfies ts.CompilerOptions;
189179

190-
const resolveLibs = (params: ProgramFactoryParams): readonly LibFile[] => {
191-
const require = createRequire(params.configPath);
192-
const request = 'typescript/package.json';
193-
let tsPath: string;
194-
try {
195-
tsPath = path.dirname(
196-
require.resolve(request, {
197-
paths: [
198-
path.join(path.dirname(params.configPath), 'node_modules'),
199-
path.join(params.rootPath, 'node_modules'),
200-
...(require.resolve.paths(request) || []),
201-
],
202-
})
203-
);
204-
} catch (_error) {
205-
return [];
206-
}
207-
const libs = ts.sys.readDirectory(
208-
path.resolve(tsPath, 'lib'),
209-
/*extensions*/ ['.d.ts'],
210-
/*exclude*/ ['typescript.d.ts'],
211-
/*include*/ ['lib.*'],
212-
/*depth*/ 1
213-
);
214-
const output: LibFile[] = [];
215-
for (const fileName of libs) {
216-
const contents = ts.sys.readFile(fileName, 'utf8');
217-
if (contents) output.push({ filename: path.basename(fileName), contents });
180+
const resolveDefaultLibsPath = (params: ProgramFactoryParams): string => {
181+
const target = ts.getDefaultLibFilePath({});
182+
if (!ts.sys.fileExists(target)) {
183+
const require = createRequire(params.configPath);
184+
const request = 'typescript/package.json';
185+
try {
186+
return path.dirname(
187+
require.resolve(request, {
188+
paths: [
189+
path.join(path.dirname(params.configPath), 'node_modules'),
190+
path.join(params.rootPath, 'node_modules'),
191+
...(require.resolve.paths(request) || []),
192+
],
193+
})
194+
);
195+
} catch (_error) {
196+
return path.resolve(params.rootPath, 'node_modules', 'typescript', 'lib');
197+
}
198+
} else {
199+
return path.dirname(target);
218200
}
219-
return output;
220201
};
221202

222203
const resolveConfig = (params: ProgramFactoryParams, system: ts.System): ts.ParsedCommandLine => {

packages/internal/src/config.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ export interface GraphQLSPConfig {
1212
}
1313

1414
export const parseConfig = (
15-
input: Record<string, unknown>,
15+
input: unknown,
1616
/** Defines the path of the "main" `tsconfig.json` file.
1717
* @remarks
1818
* This should be the `rootPath` output from `loadConfig`,
1919
* which is the path of the user's `tsconfig.json` before
2020
* resolving `extends` options.
2121
*/
2222
rootPath: string = process.cwd()
23-
) => {
23+
): GraphQLSPConfig => {
2424
const resolveConfigDir = (input: string | undefined) => {
2525
if (!input) return input;
2626
return path.normalize(
@@ -36,7 +36,11 @@ export const parseConfig = (
3636
);
3737
};
3838

39-
if (input.schema && typeof input.schema === 'object') {
39+
if (input == null || typeof input !== 'object') {
40+
throw new TadaError(`Configuration was not loaded properly (Received: ${input})`);
41+
}
42+
43+
if ('schema' in input && input.schema && typeof input.schema === 'object') {
4044
const { schema } = input;
4145
if (!('url' in schema)) {
4246
throw new TadaError('Configuration contains a `schema` object, but no `url` property');
@@ -55,7 +59,7 @@ export const parseConfig = (
5559
"Configuration contains a `schema.headers` property, but it's not an object"
5660
);
5761
}
58-
} else if (typeof input.schema !== 'string') {
62+
} else if (!('schema' in input) || typeof input.schema !== 'string') {
5963
throw new TadaError('Configuration is missing a `schema` property');
6064
} else if (
6165
'tadaOutputLocation' in input &&
@@ -98,5 +102,5 @@ export const parseConfig = (
98102
tadaOutputLocation: resolveConfigDir(output.tadaOutputLocation),
99103
tadaTurboLocation: resolveConfigDir(output.tadaTurboLocation),
100104
tadaPersistedLocation: resolveConfigDir(output.tadaPersistedLocation),
101-
} satisfies GraphQLSPConfig;
105+
};
102106
};

packages/internal/src/resolve.ts

+29-35
Original file line numberDiff line numberDiff line change
@@ -81,46 +81,40 @@ export const loadConfig = async (targetPath?: string): Promise<LoadConfigResult>
8181
: 'No tsconfig.json found at or above current working directory'
8282
);
8383
}
84-
const tsconfig = await readTSConfigFile(rootTsconfigPath);
85-
const pluginConfig = getPluginConfig(tsconfig);
86-
if (pluginConfig) {
87-
return {
88-
pluginConfig,
89-
configPath: rootTsconfigPath,
90-
rootPath: path.dirname(rootTsconfigPath),
91-
};
92-
}
9384

94-
if (Array.isArray(tsconfig.extends)) {
95-
for (let extend of tsconfig.extends) {
96-
if (path.extname(extend) !== '.json') extend += '.json';
85+
const load = async (targetPath: string): Promise<LoadConfigResult> => {
86+
const tsconfig = await readTSConfigFile(targetPath);
87+
const pluginConfig = getPluginConfig(tsconfig);
88+
89+
if (pluginConfig) {
90+
return {
91+
pluginConfig,
92+
configPath: targetPath,
93+
rootPath: path.dirname(rootTsconfigPath),
94+
};
95+
}
96+
97+
if (Array.isArray(tsconfig.extends)) {
98+
for (let extend of tsconfig.extends) {
99+
if (path.extname(extend) !== '.json') extend += '.json';
100+
try {
101+
const tsconfigPath = await resolveExtend(extend, path.dirname(rootTsconfigPath));
102+
if (tsconfigPath) return load(tsconfigPath);
103+
} catch (_error) {}
104+
}
105+
} else if (tsconfig.extends) {
97106
try {
98-
const tsconfigPath = await resolveExtend(extend, path.dirname(rootTsconfigPath));
99-
if (tsconfigPath) {
100-
const config = loadConfig(targetPath);
101-
return {
102-
...config,
103-
rootPath: path.dirname(rootTsconfigPath),
104-
};
105-
}
107+
const tsconfigPath = await resolveExtend(tsconfig.extends, path.dirname(rootTsconfigPath));
108+
if (tsconfigPath) return load(tsconfigPath);
106109
} catch (_error) {}
107110
}
108-
} else if (tsconfig.extends) {
109-
try {
110-
const tsconfigPath = await resolveExtend(tsconfig.extends, path.dirname(rootTsconfigPath));
111-
if (tsconfigPath) {
112-
const config = loadConfig(targetPath);
113-
return {
114-
...config,
115-
rootPath: path.dirname(rootTsconfigPath),
116-
};
117-
}
118-
} catch (_error) {}
119-
}
120111

121-
throw new TadaError(
122-
`Could not find a valid GraphQLSP plugin entry in: ${maybeRelative(rootTsconfigPath)}`
123-
);
112+
throw new TadaError(
113+
`Could not find a valid GraphQLSP plugin entry in: ${maybeRelative(rootTsconfigPath)}`
114+
);
115+
};
116+
117+
return await load(rootTsconfigPath);
124118
};
125119

126120
/** @deprecated Use {@link loadConfig} instead */

0 commit comments

Comments
 (0)