Skip to content

Commit

Permalink
feat(cli): improve error message when encountering unsupported syntax
Browse files Browse the repository at this point in the history
  - Add NotFoundExportedKind error class
  - Add link for GitHub issue creation in CLI error message
  • Loading branch information
imjuni committed Oct 2, 2024
1 parent 6df0185 commit ebc3f82
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 42 deletions.
8 changes: 2 additions & 6 deletions src/cli/commands/buildCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ import { building } from '#/modules/commands/building';
import consola from 'consola';
import type yargs from 'yargs';

async function buildCommandCode(argv: yargs.ArgumentsCamelCase<TCommandBuildArgvOptions>) {
const options = await createBuildOptions(argv);
await building(options);
}

export async function buildCommand(argv: yargs.ArgumentsCamelCase<TCommandBuildArgvOptions>) {
ProgressBar.it.enable = true;
Spinner.it.enable = true;
Reasoner.it.enable = true;

try {
await buildCommandCode(argv);
const options = await createBuildOptions(argv);
await building(options);
} catch (err) {
consola.error(err);
} finally {
Expand Down
16 changes: 6 additions & 10 deletions src/cli/commands/initCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,18 @@ import { initializing } from '#/modules/commands/initializing';
import consola from 'consola';
import type yargs from 'yargs';

async function initCommandCode(argv: yargs.ArgumentsCamelCase<ICommandInitOptions>) {
const option: TCommandInitOptions = {
$kind: CE_CTIX_COMMAND.INIT_COMMAND,
forceYes: argv.forceYes,
};

await initializing(option);
}

export async function initCommand(argv: yargs.ArgumentsCamelCase<ICommandInitOptions>) {
ProgressBar.it.enable = true;
Spinner.it.enable = true;
Reasoner.it.enable = true;

try {
await initCommandCode(argv);
const option: TCommandInitOptions = {
$kind: CE_CTIX_COMMAND.INIT_COMMAND,
forceYes: argv.forceYes,
};

await initializing(option);
} catch (err) {
consola.error(err);
} finally {
Expand Down
14 changes: 4 additions & 10 deletions src/cli/commands/removeCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ import { removing } from '#/modules/commands/removing';
import consola from 'consola';
import type yargs from 'yargs';

async function removeCommandCode(
argv: yargs.ArgumentsCamelCase<TCommandRemoveOptions & TCommandBuildArgvOptions>,
) {
const options = await createBuildOptions(argv);
const removeOptions = createRemoveOptions(argv);

await removing({ ...options, ...removeOptions });
}

export async function removeCommand(
argv: yargs.ArgumentsCamelCase<TCommandRemoveOptions & TCommandBuildArgvOptions>,
) {
Expand All @@ -26,7 +17,10 @@ export async function removeCommand(
Reasoner.it.enable = true;

try {
await removeCommandCode(argv);
const options = await createBuildOptions(argv);
const removeOptions = createRemoveOptions(argv);

await removing({ ...options, ...removeOptions });
} catch (err) {
consola.error(err);
} finally {
Expand Down
17 changes: 16 additions & 1 deletion src/cli/ux/Reasoner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export class Reasoner {
}
}

get logger() {
return this.#logger;
}

get enable() {
return this.#enable;
}
Expand Down Expand Up @@ -88,7 +92,7 @@ export class Reasoner {
} else {
messageBlock.push(
` ${chevronRight} ${chalk.gray(
`${filePath}:${reason.lineAndCharacter.line}:${reason.lineAndCharacter.character}`,
`${filePath}:${chalk.yellowBright(reason.lineAndCharacter.line)}:${chalk.yellowBright(reason.lineAndCharacter.character)}`,
)}`,
);
}
Expand Down Expand Up @@ -120,6 +124,17 @@ export class Reasoner {
this.#logger(warns.join(''));
this.#streamFunc(errors.join(''));
}

displayNewIssueMessage() {
const messageIndent = ' > ';
this.#logger(
chalk.green(
`${messageIndent}Please submit a new GitHub issue with a reproducible repository to improve ctix!`,
),
);
this.#logger(chalk.green(`${messageIndent}https://github.com/imjuni/ctix/issues/new`));
this.#logger('\n');
}
}

Reasoner.bootstrap();
12 changes: 11 additions & 1 deletion src/compilers/getExportedKind.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getFunctionName } from '#/compilers/getFunctionName';
import { NotFoundExportedKind } from '#/errors/NotFoundExportedKind';
import * as tsm from 'ts-morph';
import { match } from 'ts-pattern';

Expand Down Expand Up @@ -207,7 +208,16 @@ export function getExportedKind(node: tsm.ExportedDeclarations): {
* ```
*/
.otherwise(() => {
throw new Error(`Cannot support type: (${node.getKind()}) ${node.getText()}`);
const sourceFile = node.getSourceFile();
const filePath = sourceFile.getFilePath();
const pos = sourceFile.getLineAndColumnAtPos(node.getStart(false));

throw new NotFoundExportedKind(
pos,
filePath,
node,
`Cannot support type: (${node.getKind()}) ${node.getText()}`,
);
})
);
}
51 changes: 51 additions & 0 deletions src/errors/NotFoundExportedKind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { IReason } from '#/compilers/interfaces/IReason';
import type * as tsm from 'ts-morph';

export class NotFoundExportedKind extends Error {
#pos: { line: number; column: number };

#filePath: string;

#node: tsm.Node;

get pos() {
return this.#pos;
}

Check warning on line 13 in src/errors/NotFoundExportedKind.ts

View check run for this annotation

Codecov / codecov/patch

src/errors/NotFoundExportedKind.ts#L12-L13

Added lines #L12 - L13 were not covered by tests

get filePath() {
return this.#filePath;
}

Check warning on line 17 in src/errors/NotFoundExportedKind.ts

View check run for this annotation

Codecov / codecov/patch

src/errors/NotFoundExportedKind.ts#L16-L17

Added lines #L16 - L17 were not covered by tests

get node() {
return this.#node;
}

Check warning on line 21 in src/errors/NotFoundExportedKind.ts

View check run for this annotation

Codecov / codecov/patch

src/errors/NotFoundExportedKind.ts#L20-L21

Added lines #L20 - L21 were not covered by tests

constructor(
pos: NotFoundExportedKind['pos'],
filePath: string,
node: tsm.Node,
message?: string,
) {
super(message);

this.#pos = pos;
this.#node = node;
this.#filePath = filePath;
}

get reason(): IReason {
const message =
`Cannot support export statement: (${this.#node.getKind()}) ${this.#node.getText()}`.trim();

return {
type: 'error',
lineAndCharacter: {
line: this.#pos.line,
character: this.#pos.column,
},
nodes: this.#node == null ? undefined : [this.#node],
filePath: this.#filePath,
message,
};
}

Check warning on line 50 in src/errors/NotFoundExportedKind.ts

View check run for this annotation

Codecov / codecov/patch

src/errors/NotFoundExportedKind.ts#L37-L50

Added lines #L37 - L50 were not covered by tests
}
46 changes: 42 additions & 4 deletions src/modules/commands/bundling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { isDeclarationFile } from '#/compilers/isDeclarationFile';
import { getExtendOptions } from '#/configs/getExtendOptions';
import type { TBundleOptions } from '#/configs/interfaces/TBundleOptions';
import type { TCommandBuildOptions } from '#/configs/interfaces/TCommandBuildOptions';
import { NotFoundExportedKind } from '#/errors/NotFoundExportedKind';
import { ProjectContainer } from '#/modules/file/ProjectContainer';
import { checkOutputFile } from '#/modules/file/checkOutputFile';
import { getTsExcludeFiles } from '#/modules/file/getTsExcludeFiles';
Expand All @@ -31,6 +32,8 @@ import { getRenderData } from '#/templates/modules/getRenderData';
import { getSelectStyle } from '#/templates/modules/getSelectStyle';
import chalk from 'chalk';
import dayjs from 'dayjs';
import { isError } from 'my-easy-fp';
import { fail, pass } from 'my-only-either';
import type * as tsm from 'ts-morph';

export async function bundling(buildOptions: TCommandBuildOptions, bundleOption: TBundleOptions) {
Expand Down Expand Up @@ -115,16 +118,26 @@ export async function bundling(buildOptions: TCommandBuildOptions, bundleOption:
ProgressBar.it.head = ' file ';
ProgressBar.it.start(filenames.length, 0);

const statements = (
const statementEithers = (
await Promise.all(
filenames
.map((filename) => project.getSourceFile(filename))
.filter((sourceFile): sourceFile is tsm.SourceFile => sourceFile != null)
.map(async (sourceFile) => {
const exportStatement = getExportStatement(sourceFile, bundleOption, extendOptions);
ProgressBar.it.increment();

return exportStatement;
try {
const exportStatement = await getExportStatement(
sourceFile,
bundleOption,
extendOptions,
);

return pass(exportStatement);
} catch (caught) {
const err = isError(caught, new Error('unknown error raised'));
return fail(err);
}
}),
)
).flat();
Expand All @@ -134,8 +147,33 @@ export async function bundling(buildOptions: TCommandBuildOptions, bundleOption:
const statementMap = new Map<string, IExportStatement[]>();
const statementTable = new StatementTable();

const failStatements = statementEithers
.filter((statementEither) => statementEither.type === 'fail')
.map((statementEither) => statementEither.fail)
.filter((err) => err != null);

const statements = statementEithers
.filter((statementEither) => statementEither.type === 'pass')
.map((statementEither) => statementEither.pass)
.filter((statement) => statement != null)
.flat();

statementTable.inserts(statements);

if (failStatements.length > 0) {
Spinner.it.fail("ctix 'bundle' mode incomplete ...");
Spinner.it.stop();

const reasons = failStatements
.filter((err) => err instanceof NotFoundExportedKind)
.map((err) => err.reason);

Reasoner.it.start(reasons);
Reasoner.it.displayNewIssueMessage();

return;
}

Spinner.it.start(`build ${`"${chalk.green(bundleOption.exportFilename)}"`} file start`);

statements
Expand Down Expand Up @@ -169,7 +207,7 @@ export async function bundling(buildOptions: TCommandBuildOptions, bundleOption:

Spinner.it.stop();
ProgressBar.it.head = ' export ';
ProgressBar.it.start(statements.length, 0);
ProgressBar.it.start(statementEithers.length, 0);

const datas = Array.from(statementMap.entries())
.map(([filePath, exportStatements]) => {
Expand Down
53 changes: 43 additions & 10 deletions src/modules/commands/creating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CE_GENERATION_STYLE } from '#/configs/const-enum/CE_GENERATION_STYLE';
import { getExtendOptions } from '#/configs/getExtendOptions';
import type { TCommandBuildOptions } from '#/configs/interfaces/TCommandBuildOptions';
import type { TCreateOptions } from '#/configs/interfaces/TCreateOptions';
import { NotFoundExportedKind } from '#/errors/NotFoundExportedKind';
import { getAllParentDir } from '#/modules//path/getAllParentDir';
import { ProjectContainer } from '#/modules/file/ProjectContainer';
import { checkOutputFile } from '#/modules/file/checkOutputFile';
Expand All @@ -40,7 +41,9 @@ import { getAutoRenderCase } from '#/templates/modules/getAutoRenderCase';
import { getRenderData } from '#/templates/modules/getRenderData';
import chalk from 'chalk';
import dayjs from 'dayjs';
import { isError } from 'my-easy-fp';
import { getDirnameSync } from 'my-node-fp';
import { fail, pass } from 'my-only-either';
import type * as tsm from 'ts-morph';

export async function creating(_buildOptions: TCommandBuildOptions, createOption: TCreateOptions) {
Expand Down Expand Up @@ -108,26 +111,56 @@ export async function creating(_buildOptions: TCommandBuildOptions, createOption
ProgressBar.it.head = ' file ';
ProgressBar.it.start(filenames.length, 0);

const statements = (
await Promise.all(
filenames
.map((filename) => project.getSourceFile(filename))
.filter((sourceFile): sourceFile is tsm.SourceFile => sourceFile != null)
.map(async (sourceFile) => {
const statementEithers = await Promise.all(
filenames
.map((filename) => project.getSourceFile(filename))
.filter((sourceFile): sourceFile is tsm.SourceFile => sourceFile != null)
.map(async (sourceFile) => {
ProgressBar.it.increment();

try {
const statement = await getExportStatement(sourceFile, createOption, extendOptions);
return statement;
}),
)
).flat();
return pass(statement);
} catch (caught) {
const err = isError(caught, new Error('unknown error raised'));
return fail(err);
}
}),
);

ProgressBar.it.stop();

const filePathMap = new Map<string, IExportStatement[]>();
const dirPathMap = new Map<string, IExportStatement[]>();
const statementTable = new StatementTable();

const failStatements = statementEithers
.filter((statementEither) => statementEither.type === 'fail')
.map((statementEither) => statementEither.fail)
.filter((err) => err != null);

const statements = statementEithers
.filter((statementEither) => statementEither.type === 'pass')
.map((statementEither) => statementEither.pass)
.filter((statement) => statement != null)
.flat();

statementTable.inserts(statements);

if (failStatements.length > 0) {
Spinner.it.fail("ctix 'create' mode incomplete ...");
Spinner.it.stop();

const reasons = failStatements
.filter((err) => err instanceof NotFoundExportedKind)
.map((err) => err.reason);

Reasoner.it.start(reasons);
Reasoner.it.displayNewIssueMessage();

return;
}

Spinner.it.start(`build ${`"${chalk.green(createOption.exportFilename)}"`} file start`);

statements
Expand Down

0 comments on commit ebc3f82

Please sign in to comment.