Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli-utils): Complete CLI output, logging, and config refactor #200

Merged
merged 31 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
09e10f5
Replace with ts-native config parsing and refactor utilities
kitten Apr 14, 2024
eb97adb
Remove json5 from @gql.tada/internal
kitten Apr 14, 2024
bb9f5eb
Export errors
kitten Apr 14, 2024
fcc1162
Delete unused src/resolve.ts
kitten Apr 14, 2024
3c2def2
Add config parsing to @gql.tada/internal
kitten Apr 14, 2024
32455e4
Add new exports
kitten Apr 14, 2024
220baf3
Update `check` command to use new config loader
kitten Apr 14, 2024
d73f4ac
Improve TSError output
kitten Apr 14, 2024
4b903d5
Add error output to `generate output` command
kitten Apr 14, 2024
a5d2ce3
Add error output to `generate schema` command
kitten Apr 14, 2024
60238e8
Add writing utility
kitten Apr 14, 2024
0060ee7
Add error output to `generate turbo` command
kitten Apr 15, 2024
df30c36
Add error output to `generate persisted` command
kitten Apr 15, 2024
a5271cd
Replace `getLineCol` and add end ranges for GitHub
kitten Apr 15, 2024
f09c1fd
Add threading to `generate persisted` command
kitten Apr 15, 2024
71421cb
Clean up check logger
kitten Apr 15, 2024
8d24885
Fix output code of check command
kitten Apr 15, 2024
eebfd46
Add log output to `generate-persisted`
kitten Apr 15, 2024
cc66496
Add threading to `turbo` command
kitten Apr 15, 2024
5c0669b
Add log output to `turbo` command
kitten Apr 15, 2024
d3bd064
Add missing success messages
kitten Apr 15, 2024
397763a
Add missing type annotations
kitten Apr 15, 2024
f9837db
Fix semiver not supporting semver ranges
kitten Apr 15, 2024
c7f3d16
Replace manual config, tsconfig, and schema loader checks in doctor
kitten Apr 15, 2024
7af2f69
Replace json5 in init command
kitten Apr 15, 2024
980ac63
Remove json5
kitten Apr 15, 2024
8093149
Add changesets
kitten Apr 15, 2024
c13dde3
Remove unused files
kitten Apr 15, 2024
c7a4629
Update LICENSEs
kitten Apr 15, 2024
327a3ab
Fix typo in turbo/runner
kitten Apr 15, 2024
8e5ae5c
Add warning for default turbo location
kitten Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-taxis-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gql.tada/cli-utils": major
---

Add stylised log output and threading to commands.
5 changes: 5 additions & 0 deletions .changeset/thin-bikes-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gql.tada/cli-utils": minor
---

Add annotations for GitHub actions to command outputs that report diagnostics.
5 changes: 5 additions & 0 deletions .changeset/twelve-kiwis-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gql.tada/internal": minor
---

Implement new config resolution helpers
26 changes: 0 additions & 26 deletions packages/cli-utils/LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,32 +120,6 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

## json5

MIT License

Copyright (c) 2012-2018 Aseem Kishore, and [others].

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

[others]: https://github.com/json5/json5/contributors

## merge-stream

The MIT License (MIT)
Expand Down
1 change: 0 additions & 1 deletion packages/cli-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"@types/node": "^20.11.0",
"clipanion": "4.0.0-rc.3",
"execa": "^8.0.1",
"json5": "^2.2.3",
"rollup": "^4.9.4",
"sade": "^1.8.1",
"semiver": "^1.1.0",
Expand Down
34 changes: 8 additions & 26 deletions packages/cli-utils/src/commands/check/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import * as t from '../../term';
import type { DiagnosticMessage } from './types';
import type { SeveritySummary } from './types';

const CWD = process.cwd();
export * from '../shared/logger';
import { indent } from '../shared/logger';

export function code(text: string) {
return t.text`${t.cmd(t.CSI.Style, t.Style.Underline)}${text}${t.cmd(
t.CSI.Style,
t.Style.NoUnderline
)}`;
}
const CWD = process.cwd();
const INDENT = ' ';

export function diagnosticFile(filePath: string) {
const relativePath = path.relative(CWD, filePath);
Expand All @@ -26,8 +23,6 @@ export function diagnosticFile(filePath: string) {
}

export function diagnosticMessage(message: DiagnosticMessage) {
const indent = t.Chars.Space.repeat(2);

let color = t.Style.Foreground;
if (message.severity === 'info') {
color = t.Style.BrightBlue;
Expand All @@ -37,21 +32,16 @@ export function diagnosticMessage(message: DiagnosticMessage) {
color = t.Style.BrightRed;
}

let text = message.message.trim();
if (text.includes('\n')) {
text = text.split('\n').join(t.text([t.Chars.Newline, indent, t.Chars.Tab, t.Chars.Tab]));
}

return t.text([
indent,
INDENT,
t.cmd(t.CSI.Style, t.Style.BrightBlack),
`${message.line}:${message.col}`,
t.Chars.Tab,
t.cmd(t.CSI.Style, color),
message.severity,
t.Chars.Tab,
t.cmd(t.CSI.Style, t.Style.Foreground),
text,
indent(message.message.trim(), t.text([INDENT, t.Chars.Tab, t.Chars.Tab])),
t.Chars.Newline,
]);
}
Expand Down Expand Up @@ -95,6 +85,8 @@ export function diagnosticMessageGithub(message: DiagnosticMessage): void {
file: message.file,
line: message.line,
col: message.col,
endLine: message.endLine,
endColumn: message.endColumn,
});
}

Expand All @@ -115,13 +107,3 @@ export function runningDiagnostics(file: number, ofFiles?: number) {
})
);
}

export function errorMessage(message: string) {
return t.error([
'\n',
t.cmd(t.CSI.Style, [t.Style.Red, t.Style.Invert]),
` ${t.Icons.Warning} Error `,
t.cmd(t.CSI.Style, t.Style.NoInvert),
`\n${message.trim()}\n`,
]);
}
45 changes: 15 additions & 30 deletions packages/cli-utils/src/commands/check/runner.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import path from 'node:path';
import type { GraphQLSPConfig, LoadConfigResult } from '@gql.tada/internal';
import { loadConfig, parseConfig } from '@gql.tada/internal';

import { getGraphQLSPConfig } from '../../lsp';
import { getTsConfig } from '../../tsconfig';
import * as logger from './logger';

import type { ComposeInput } from '../../term';
import type { Severity, SeveritySummary } from './types';

Expand Down Expand Up @@ -33,37 +31,24 @@ export interface Options {
}

export async function* run(opts: Options): AsyncIterable<ComposeInput> {
const CWD = process.cwd();
const { runDiagnostics } = await import('./thread');

const tsconfig = await getTsConfig(opts.tsconfig);
if (!tsconfig) {
const relative = opts.tsconfig
? logger.code(path.relative(process.cwd(), opts.tsconfig))
: 'the current working directory';
throw logger.errorMessage(
`The ${logger.code('tsconfig.json')} file at ${relative} could not be loaded.\n`
);
}

const config = getGraphQLSPConfig(tsconfig);
if (!config) {
throw logger.errorMessage(
`No ${logger.code('"@0no-co/graphqlsp"')} plugin was found in your ${logger.code(
'tsconfig.json'
)}.\n`
);
let configResult: LoadConfigResult;
let pluginConfig: GraphQLSPConfig;
try {
configResult = await loadConfig(opts.tsconfig);
pluginConfig = parseConfig(configResult.pluginConfig);
} catch (error) {
throw logger.externalError('Failed to load configuration.', error);
}

let tsconfigPath = opts.tsconfig || CWD;
tsconfigPath =
path.extname(tsconfigPath) !== '.json'
? path.resolve(CWD, tsconfigPath, 'tsconfig.json')
: path.resolve(CWD, tsconfigPath);

const summary: SeveritySummary = { warn: 0, error: 0, info: 0 };
const minSeverity = opts.minSeverity;
const generator = runDiagnostics({ tsconfigPath, config });
const generator = runDiagnostics({
rootPath: configResult.rootPath,
configPath: configResult.configPath,
pluginConfig,
});

let totalFileCount = 0;
let fileCount = 0;
Expand All @@ -90,7 +75,7 @@ export async function* run(opts: Options): AsyncIterable<ComposeInput> {
yield logger.runningDiagnostics(++fileCount, totalFileCount);
}
} catch (error: any) {
throw logger.errorMessage(error.message || `${error}`);
throw logger.externalError('Could not check files', error);
}

// Reset notice count if it's outside of min severity
Expand Down
61 changes: 26 additions & 35 deletions packages/cli-utils/src/commands/check/thread.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import * as path from 'node:path';
import { Project, ts } from 'ts-morph';

import { load, resolveTypeScriptRootDir } from '@gql.tada/internal';
import type { GraphQLSPConfig } from '@gql.tada/internal';
import { load } from '@gql.tada/internal';
import { init, getGraphQLDiagnostics } from '@0no-co/graphqlsp/api';

import type { GraphQLSPConfig } from '../../lsp';
import { createPluginInfo } from '../../ts/project';
import { createPluginInfo, getFilePosition } from '../../ts';
import { expose } from '../../threads';

import type { Severity, DiagnosticMessage, DiagnosticSignal } from './types';

const getLineCol = (text: string, start: number | undefined): [number, number] => {
if (text && start) {
let counter = 0;
const parts = text.split('\n');
for (let i = 0; i <= parts.length; i++) {
const line = parts[i];
if (counter + line.length > start) {
return [i + 1, start + 1 - counter];
} else {
counter = counter + (line.length + 1);
continue;
}
}
}
return [0, 0];
};

const loadSchema = async (rootPath: string, config: GraphQLSPConfig) => {
const loader = load({ origin: config.schema, rootPath });
const result = await loader.load();
Expand All @@ -35,53 +18,61 @@ const loadSchema = async (rootPath: string, config: GraphQLSPConfig) => {
};

export interface DiagnosticsParams {
config: GraphQLSPConfig;
tsconfigPath: string;
rootPath: string;
configPath: string;
pluginConfig: GraphQLSPConfig;
}

async function* _runDiagnostics(
params: DiagnosticsParams
): AsyncIterableIterator<DiagnosticSignal> {
init({ typescript: ts as any });
const projectPath = path.dirname(params.tsconfigPath);
const rootPath = (await resolveTypeScriptRootDir(params.tsconfigPath)) || params.tsconfigPath;
const schemaRef = await loadSchema(rootPath, params.config);
const project = new Project({ tsConfigFilePath: params.tsconfigPath });
const pluginInfo = createPluginInfo(project, params.config, projectPath);
const sourceFiles = project.getSourceFiles();
const projectPath = path.dirname(params.configPath);
const schemaRef = await loadSchema(projectPath, params.pluginConfig);
const project = new Project({ tsConfigFilePath: params.configPath });
const pluginInfo = createPluginInfo(project, params.pluginConfig, projectPath);

// Filter source files by whether they're under the relevant root path
const sourceFiles = project.getSourceFiles().filter((sourceFile) => {
const filePath = path.resolve(projectPath, sourceFile.getFilePath());
const relative = path.relative(params.rootPath, filePath);
return !relative.startsWith('..');
});

yield {
kind: 'FILE_COUNT',
fileCount: sourceFiles.length,
};

for (const sourceFile of sourceFiles) {
const filePath = sourceFile.getFilePath();
for (const { compilerNode: sourceFile } of sourceFiles) {
const filePath = sourceFile.fileName;
const diagnostics = getGraphQLDiagnostics(filePath, schemaRef, pluginInfo);
const messages: DiagnosticMessage[] = [];

if (diagnostics && diagnostics.length) {
const sourceText = sourceFile.getText();
for (const diagnostic of diagnostics) {
if (
!('messageText' in diagnostic) ||
typeof diagnostic.messageText !== 'string' ||
!diagnostic.file
)
) {
continue;
}
let severity: Severity = 'info';
if (diagnostic.category === ts.DiagnosticCategory.Error) {
severity = 'error';
} else if (diagnostic.category === ts.DiagnosticCategory.Warning) {
severity = 'warn';
}
const [line, col] = getLineCol(sourceText, diagnostic.start);
const position = getFilePosition(sourceFile, diagnostic.start, diagnostic.length);
messages.push({
severity,
message: diagnostic.messageText,
file: diagnostic.file.fileName,
line,
col,
line: position.line,
col: position.col,
endLine: position.endLine,
endColumn: position.endColumn,
});
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-utils/src/commands/check/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export interface DiagnosticMessage {
file: string;
line: number;
col: number;
endLine: number | undefined;
endColumn: number | undefined;
}

export interface FileDiagnosticsSignal {
Expand Down
22 changes: 1 addition & 21 deletions packages/cli-utils/src/commands/doctor/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,7 @@ import { pipe, interval, map } from 'wonka';

import * as t from '../../term';

export function code(text: string) {
return t.text`${t.cmd(t.CSI.Style, t.Style.Underline)}${text}${t.cmd(
t.CSI.Style,
t.Style.NoUnderline
)}`;
}

export function bold(text: string) {
return t.text`${t.cmd(t.CSI.Style, t.Style.Bold)}${text}${t.cmd(t.CSI.Style, t.Style.Normal)}`;
}

export function hint(text: string) {
return t.text([
t.cmd(t.CSI.Style, t.Style.BrightBlack),
`${t.HeavyBox.BottomLeft} `,
t.cmd(t.CSI.Style, t.Style.BrightBlue),
`${t.Icons.Info} `,
t.cmd(t.CSI.Style, t.Style.Blue),
text,
]);
}
export * from '../shared/logger';

export function console(error: any) {
return t.text([
Expand Down
Loading
Loading