|
| 1 | +import * as path from 'path'; |
| 2 | +import sade from 'sade'; |
| 3 | +import { URI } from 'vscode-uri'; |
| 4 | + |
| 5 | +export interface SvelteCheckCliOptions { |
| 6 | + workspaceUri: URI; |
| 7 | + outputFormat: OutputFormat; |
| 8 | + watch: boolean; |
| 9 | + tsconfig?: string; |
| 10 | + filePathsToIgnore: string[]; |
| 11 | + failOnWarnings: boolean; |
| 12 | + failOnHints: boolean; |
| 13 | + compilerWarnings: Record<string, 'error' | 'ignore'>; |
| 14 | + diagnosticSources: DiagnosticSource[]; |
| 15 | + threshold: Threshold; |
| 16 | +} |
| 17 | + |
| 18 | +// eslint-disable max-len |
| 19 | +export function parseOptions(cb: (opts: SvelteCheckCliOptions) => any) { |
| 20 | + const prog = sade('svelte-check', true) |
| 21 | + .version('1.x') |
| 22 | + .option( |
| 23 | + '--workspace', |
| 24 | + 'Path to your workspace. All subdirectories except node_modules and those listed in `--ignore` are checked' |
| 25 | + ) |
| 26 | + .option( |
| 27 | + '--output', |
| 28 | + 'What output format to use. Options are human, human-verbose, machine.', |
| 29 | + 'human-verbose' |
| 30 | + ) |
| 31 | + .option( |
| 32 | + '--watch', |
| 33 | + 'Will not exit after one pass but keep watching files for changes and rerun diagnostics', |
| 34 | + false |
| 35 | + ) |
| 36 | + .option( |
| 37 | + '--tsconfig', |
| 38 | + 'Pass a path to a tsconfig or jsconfig file. The path can be relative to the workspace path or absolute. Doing this means that only files matched by the files/include/exclude pattern of the config file are diagnosed. It also means that errors from TypeScript and JavaScript files are reported.' |
| 39 | + ) |
| 40 | + .option( |
| 41 | + '--ignore', |
| 42 | + 'Files/folders to ignore - relative to workspace root, comma-separated, inside quotes. Example: `--ignore "dist,build"`' |
| 43 | + ) |
| 44 | + .option( |
| 45 | + '--fail-on-warnings', |
| 46 | + 'Will also exit with error code when there are warnings', |
| 47 | + false |
| 48 | + ) |
| 49 | + .option('--fail-on-hints', 'Will also exit with error code when there are hints', false) |
| 50 | + .option( |
| 51 | + '--compiler-warnings', |
| 52 | + 'A list of Svelte compiler warning codes. Each entry defines whether that warning should be ignored or treated as an error. Warnings are comma-separated, between warning code and error level is a colon; all inside quotes. Example: `--compiler-warnings "css-unused-selector:ignore,unused-export-let:error"`' |
| 53 | + ) |
| 54 | + .option( |
| 55 | + '--diagnostic-sources', |
| 56 | + 'A list of diagnostic sources which should run diagnostics on your code. Possible values are `js` (includes TS), `svelte`, `css`. Comma-separated, inside quotes. By default all are active. Example: `--diagnostic-sources "js,svelte"`' |
| 57 | + ) |
| 58 | + .option( |
| 59 | + '--threshold', |
| 60 | + 'Filters the diagnostics to display. `error` will output only errors while `warning` will output warnings and errors.', |
| 61 | + 'hint' |
| 62 | + ) |
| 63 | + .action((opts) => { |
| 64 | + const workspaceUri = getWorkspaceUri(opts); |
| 65 | + cb({ |
| 66 | + workspaceUri, |
| 67 | + outputFormat: getOutputFormat(opts), |
| 68 | + watch: !!opts.watch, |
| 69 | + tsconfig: getTsconfig(opts, workspaceUri.fsPath), |
| 70 | + filePathsToIgnore: getFilepathsToIgnore(opts), |
| 71 | + failOnWarnings: !!opts['fail-on-warnings'], |
| 72 | + failOnHints: !!opts['fail-on-hints'], |
| 73 | + compilerWarnings: getCompilerWarnings(opts), |
| 74 | + diagnosticSources: getDiagnosticSources(opts), |
| 75 | + threshold: getThreshold(opts) |
| 76 | + }); |
| 77 | + }); |
| 78 | + |
| 79 | + prog.parse(process.argv); |
| 80 | +} |
| 81 | +// eslint-enable max-len |
| 82 | + |
| 83 | +const outputFormats = ['human', 'human-verbose', 'machine'] as const; |
| 84 | +type OutputFormat = typeof outputFormats[number]; |
| 85 | + |
| 86 | +function getOutputFormat(opts: Record<string, any>): OutputFormat { |
| 87 | + return outputFormats.includes(opts.output) ? opts.output : 'human-verbose'; |
| 88 | +} |
| 89 | + |
| 90 | +function getWorkspaceUri(opts: Record<string, any>) { |
| 91 | + let workspaceUri; |
| 92 | + let workspacePath = opts.workspace; |
| 93 | + if (workspacePath) { |
| 94 | + if (!path.isAbsolute(workspacePath)) { |
| 95 | + workspacePath = path.resolve(process.cwd(), workspacePath); |
| 96 | + } |
| 97 | + workspaceUri = URI.file(workspacePath); |
| 98 | + } else { |
| 99 | + workspaceUri = URI.file(process.cwd()); |
| 100 | + } |
| 101 | + return workspaceUri; |
| 102 | +} |
| 103 | + |
| 104 | +function getTsconfig(myArgs: Record<string, any>, workspacePath: string) { |
| 105 | + let tsconfig: string | undefined = myArgs.tsconfig; |
| 106 | + if (tsconfig && !path.isAbsolute(tsconfig)) { |
| 107 | + tsconfig = path.join(workspacePath, tsconfig); |
| 108 | + } |
| 109 | + return tsconfig; |
| 110 | +} |
| 111 | + |
| 112 | +function getCompilerWarnings(opts: Record<string, any>) { |
| 113 | + return stringToObj(opts['compiler-warnings']); |
| 114 | + |
| 115 | + function stringToObj(str = '') { |
| 116 | + return str |
| 117 | + .split(',') |
| 118 | + .map((s) => s.trim()) |
| 119 | + .filter((s) => !!s) |
| 120 | + .reduce((settings, setting) => { |
| 121 | + const [name, val] = setting.split(':'); |
| 122 | + if (val === 'error' || val === 'ignore') { |
| 123 | + settings[name] = val; |
| 124 | + } |
| 125 | + return settings; |
| 126 | + }, <Record<string, 'error' | 'ignore'>>{}); |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +const diagnosticSources = ['js', 'css', 'svelte'] as const; |
| 131 | +type DiagnosticSource = typeof diagnosticSources[number]; |
| 132 | + |
| 133 | +function getDiagnosticSources(opts: Record<string, any>): DiagnosticSource[] { |
| 134 | + const sources = opts['diagnostic-sources']; |
| 135 | + return sources |
| 136 | + ? sources |
| 137 | + .split(',') |
| 138 | + ?.map((s: string) => s.trim()) |
| 139 | + .filter((s: any) => diagnosticSources.includes(s)) |
| 140 | + : diagnosticSources; |
| 141 | +} |
| 142 | + |
| 143 | +function getFilepathsToIgnore(opts: Record<string, any>): string[] { |
| 144 | + return opts.ignore?.split(',') || []; |
| 145 | +} |
| 146 | + |
| 147 | +const thresholds = ['hint', 'warning', 'error'] as const; |
| 148 | +type Threshold = typeof thresholds[number]; |
| 149 | + |
| 150 | +function getThreshold(opts: Record<string, any>): Threshold { |
| 151 | + return thresholds.includes(opts.threshold) ? opts.threshold : 'hint'; |
| 152 | +} |
0 commit comments