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

Add types to options.js #126

Closed
wants to merge 13 commits into from
4 changes: 2 additions & 2 deletions lib/anonymize.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ module.exports = {
/**
* Strip the version and paths out of the given string.
* This is only used for tests to make them pass even when the version/paths change.
* @param {Options} options
* @param {Options | null} options
* @param {string} string
* @returns {string}
*/
function pathsAndVersions(options, string) {
if (options.forTests) {
if (options && options.forTests) {
const root = path.dirname(__dirname);
return replaceVersion(string.split(root).join('<local-path>'));
}
Expand Down
3 changes: 2 additions & 1 deletion lib/elm-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const Cache = require('./cache');

/**
* @typedef { import("./types/options").Options } Options
* @typedef { import("./types/options").OptionsForRunningElmReview } OptionsForRunningElmReview
* @typedef { import("./types/path").Path } Path
* @typedef { import("./types/content").ElmFile } ElmFile
* @typedef { import("./types/content").Readme } Readme
Expand All @@ -41,7 +42,7 @@ function flatMap(array, fn) {

/**
* Get all files from the project.
* @param {Options} options
* @param {OptionsForRunningElmReview} options
* @param {string} elmSyntaxVersion
* @returns {Promise<ProjectFiles>}
*/
Expand Down
9 changes: 6 additions & 3 deletions lib/error-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ class CustomError extends Error {
}

/**
* @param {Options} options
* @param {Options | null} options
* @param {CustomError} err
* @param {Path} [defaultPath]
* @returns {string}
*/
function report(options, err, defaultPath) {
if (options.report === 'json') {
if (options && options.report === 'json') {
henriquecbuss marked this conversation as resolved.
Show resolved Hide resolved
return Anonymize.pathsAndVersions(
options,
Anonymize.pathsAndVersions(
Expand All @@ -42,7 +42,10 @@ function report(options, err, defaultPath) {
);
}

return Anonymize.pathsAndVersions(options, formatHuman(options.debug, err));
return Anonymize.pathsAndVersions(
options,
formatHuman((options && options.debug) || false, err)
);
}

/**
Expand Down
23 changes: 16 additions & 7 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,27 @@ function errorHandler(err) {
async function runElmReview() {
const {elmModulePath, reviewElmJson, appHash} = await Builder.build(options);

if (!elmModulePath) {
if (!elmModulePath || !options.elmJsonPath) {
AppState.exitRequested(1);
return;
}

const optionsWithElmJsonPath = {...options, elmJsonPath: options.elmJsonPath};
Comment on lines +52 to +57
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, we just exit if elmJsonPath is null. We could have better errors if you want (if so, we could wrap this in a function and just call it where needed) 🙂


const [{app}] = await Promise.all([
Builder.buildElmParser(options, reviewElmJson).then(() =>
Runner.initializeApp(options, elmModulePath, reviewElmJson, appHash)
Builder.buildElmParser(optionsWithElmJsonPath, reviewElmJson).then(() =>
Runner.initializeApp(
optionsWithElmJsonPath,
elmModulePath,
reviewElmJson,
appHash
)
),

ResultCache.load(options, appHash)
ResultCache.load(optionsWithElmJsonPath, appHash)
]);

const success = await Runner.runReview(options, app);
const success = await Runner.runReview(optionsWithElmJsonPath, app);
AppState.exitRequested(success ? 0 : 1);
}

Expand All @@ -72,16 +79,18 @@ async function runElmReviewInWatchMode() {
await Builder.build(options);
await Builder.buildElmParser(options, reviewElmJson);

if (!elmModulePath) {
if (!elmModulePath || !options.elmJsonPath) {
Watch.watchConfiguration(options, reviewElmJson, reviewElmJsonPath, () => {
AppState.buildRestarted();
runElmReviewInWatchMode();
});
return;
}

const optionsWithElmJsonPath = {...options, elmJsonPath: options.elmJsonPath};

const initialization = await Runner.initializeApp(
options,
optionsWithElmJsonPath,
elmModulePath,
reviewElmJson,
appHash
Expand Down
93 changes: 79 additions & 14 deletions lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,25 @@ function compute(processArgv) {
alias: Flags.flags
.filter((flag) => flag.alias)
.reduce((object, flag) => {
object[flag.name] = flag.alias;
if (flag.alias) {
object[flag.name] = flag.alias;
Comment on lines +49 to +50
Copy link
Owner

@jfmengels jfmengels May 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There has to be a way to tell TS that flag.alias is always false, right? 🤔
(Not that I necessarily want to remove it, I'm mostly intellectually curious 😅 )

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's what I meant. I don't know how smart ts-reset makes filter in this case, but I'd imagine that in this case TS will not remember that flag.alias is non-falsy. If it does, then yes, otherwise, it's so close 😁

Copy link
Contributor

@lishaduck lishaduck Jun 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it locally, I tossed it into a gist:
https://gist.github.com/lishaduck/8cabda9ae913535f702a4376712d4608

I'd just give a patch, but I'm tweaking some other types. I'll PR soon (unless I forget 😉) EDIT: #155
I'm planning on rebasing that on this.

It's a patch now!

}

return object;
}, {}),
}, /** @type {Record<string, string>} */ ({})),
boolean: Flags.flags
.filter((flag) => flag.boolean)
.map((flag) => flag.name),
default: {
color: true,
details: true
},
unknown: containsHelp ? () => true : unknownCheck()
unknown: containsHelp
? () => true
: () => {
unknownCheck();
return true;
}
});

/**
Expand Down Expand Up @@ -289,14 +297,24 @@ try re-running it with ${chalk.cyan('--elmjson <path-to-elm.json>')}.`,
};
}

/**
*
* @param {minimist.ParsedArgs} args
* @returns {boolean}
*/
function parseUnsuppress(args) {
if (args.unsuppress) {
return true;
}

return listOfStrings(args['unsuppress-rules']) || false;
return Boolean(listOfStrings(args['unsuppress-rules']));
}

/**
*
* @param {string | string[]} input
* @returns {string[] | null}
*/
function listOfStrings(input) {
if (typeof input === 'string') {
return input.split(',');
Expand All @@ -305,13 +323,19 @@ function listOfStrings(input) {
if (Array.isArray(input)) {
return input.reduce(
(acc, subArray) => [...acc, ...subArray.split(',')],
[]
/** @type {string[]} */ ([])
);
}

return null;
}

/**
*
* @param {Subcommand | null} subcommand
* @param {string} string
* @returns {Template}
*/
function parseTemplate(subcommand, string) {
const match = /([^/]+\/[^#/]+)(\/[^#]+)?(#(.+))?/.exec(string);
if (!match) {
Expand All @@ -338,6 +362,12 @@ ${Flags.buildFlag(subcommand, Flags.templateFlag)}`
};
}

/**
*
* @param {minimist.ParsedArgs} args
* @param {Subcommand | null} subcommand
* @returns {string | null}
*/
function findElmJsonPath(args, subcommand) {
if (args.elmjson) return args.elmjson;
// Shortcutting the search for elm.json when `--help` since we won't need it
Expand All @@ -355,14 +385,19 @@ function findElmJsonPath(args, subcommand) {
return findUp.sync('elm.json') || null;
}

/**
*
* @returns {(flag: string) => void}
*/
function unknownCheck() {
const allFlagNames = new Set(Flags.flags.map((flag) => flag.name));

return (flag) => {
const flagRegex = /^--(?<name>[^=]*)/;
const match = flagRegex.exec(flag);
if (containsHelp || !match || !match.groups) {
return unknownShortHandFlagCheck(flag);
unknownShortHandFlagCheck(flag);
return;
}

const {name} = match.groups;
Expand All @@ -371,11 +406,14 @@ function unknownCheck() {
new ErrorMessage.CustomError('UNKNOWN FLAG', unknownFlagMessage(name))
);
}

return true;
};
}

/**
*
* @param {string} name
* @returns {string}
*/
function unknownFlagMessage(name) {
if (name === 'suppress') {
const orange = chalk.keyword('orange');
Expand Down Expand Up @@ -403,18 +441,23 @@ function unknownFlagMessage(name) {
].join('\n');
}

/**
*
* @param {string} flag
* @returns {void}
*/
function unknownShortHandFlagCheck(flag) {
const flagRegex = /^-(?<name>\w+)/;
const match = flagRegex.exec(flag);

if (!match || !match.groups) {
return true;
return;
}

const flags = match.groups.name.split('');
if (containsHelp || flags.includes('h')) {
containsHelp = true;
return true;
return;
}

const aliases = Flags.flags.map((flag) => flag.alias).filter(Boolean);
Expand All @@ -438,10 +481,13 @@ function unknownShortHandFlagCheck(flag) {
);
}
});

return true;
}

/**
*
* @param {string} flag
* @returns {string[]}
*/
function suggestions(flag) {
return Flags.flags
.map((f) => ({
Expand All @@ -463,6 +509,11 @@ function suggestions(flag) {
.map((f) => chalk.greenBright(` --${f.name}${Flags.buildFlagsArgs(f)}`));
}

/**
*
* @param {Subcommand | null} subcommand
* @param {minimist.ParsedArgs} args
*/
function checkForMissingArgs(subcommand, args) {
Flags.flags
.filter((flag) => flag.boolean === false)
Expand Down Expand Up @@ -502,6 +553,11 @@ ${Flags.buildFlag(subcommand, flag)}`
});
}

/**
*
* @param {Subcommand | null} subcommand
* @param {minimist.ParsedArgs} args
*/
function checkForInvalidArgs(subcommand, args) {
if (args.report && !['json', 'ndjson', 'human'].includes(args.report)) {
reportErrorAndExit(
Expand All @@ -519,6 +575,12 @@ ${Flags.buildFlag(subcommand, Flags.reportFlag)}`
}
}

/**
*
* @param {Subcommand | null} subcommand
* @param {string} gitHubAuth
* @returns {{ gitHubUser: string, gitHubPassword: string }}
*/
function parseGitHubAuth(subcommand, gitHubAuth) {
const split = gitHubAuth.split(':');
if (split.length !== 2) {
Expand All @@ -540,9 +602,12 @@ ${Flags.buildFlag(subcommand, Flags.gitHubAuthFlag)}`
return {gitHubUser, gitHubPassword};
}

/**
*
* @param {ErrorMessage.CustomError} errorToReport
henriquecbuss marked this conversation as resolved.
Show resolved Hide resolved
*/
function reportErrorAndExit(errorToReport) {
// @ts-expect-error - Handle this later
console.log(ErrorMessage.report({}, errorToReport));
console.log(ErrorMessage.report(null, errorToReport));

process.exit(1);
}
Expand Down
10 changes: 7 additions & 3 deletions lib/types/options.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type Options = {
forceBuild: boolean;
report: ReportMode;
reportOnOneLine: boolean;
rulesFilter: string[];
rulesFilter: string[] | null;
ignoredDirs: string[];
ignoredFiles: string[];
ignoreProblematicDependencies: boolean;
Expand All @@ -48,9 +48,9 @@ export type Options = {
templateElmModulePath: (string) => Path;
pathToTemplateElmJson: (string) => Path;
dependenciesCachePath: () => Path;
elmJsonPath: Path;
elmJsonPath: Path | null;
elmJsonPathWasSpecified: boolean;
readmePath: Path;
readmePath: Path | null;
projectToReview: () => Path;
directoriesToAnalyze: Path[];
fileCachePath: () => Path;
Expand All @@ -60,6 +60,10 @@ export type Options = {
gitHubPassword: string | undefined;
};

export type OptionsForRunningElmReview = Omit<Options, 'elmJsonPath'> & {
elmJsonPath: Path;
};

Comment on lines +63 to +66
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we think more types are going to have to change for running elm-review, we could also abstract this a little:

type OverriddenFieldsForRunningElmReview = { elmJsonPath: Path, ... }

export type OptionsForRunningElmReview = Omit<Options, keyof OverriddenFieldsForRunningElmReview> & OverriddenFieldsForRunningElmReview

Also, this name is pretty long, but I couldn't think of anything better 😅

export type DetailsMode = 'without-details' | 'with-details';

export type ReportMode = 'json' | null;
Expand Down
Loading