Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cli.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. 'use strict';
  2. const chalk = require('chalk');
  3. const checkInvalidCLIOptions = require('./utils/checkInvalidCLIOptions');
  4. const EOL = require('os').EOL;
  5. const getFormatterOptionsText = require('./utils/getFormatterOptionsText');
  6. const getModulePath = require('./utils/getModulePath');
  7. const getStdin = require('get-stdin');
  8. const meow = require('meow');
  9. const path = require('path');
  10. const printConfig = require('./printConfig');
  11. const resolveFrom = require('resolve-from');
  12. const standalone = require('./standalone');
  13. const writeOutputFile = require('./writeOutputFile');
  14. const EXIT_CODE_ERROR = 2;
  15. /**
  16. * @typedef {object} CLIFlags
  17. * @property {boolean} [cache]
  18. * @property {string} [cacheLocation]
  19. * @property {string | false} config
  20. * @property {string} [configBasedir]
  21. * @property {string} [customSyntax]
  22. * @property {string} [printConfig]
  23. * @property {string} [color]
  24. * @property {string} [customFormatter]
  25. * @property {boolean} [disableDefaultIgnores]
  26. * @property {boolean} [fix]
  27. * @property {string} [formatter="string"]
  28. * @property {string} [help]
  29. * @property {boolean} [ignoreDisables]
  30. * @property {string} [ignorePath]
  31. * @property {string[]} [ignorePattern]
  32. * @property {string} [noColor]
  33. * @property {string} [outputFile]
  34. * @property {boolean} [stdin]
  35. * @property {string} [stdinFilename]
  36. * @property {boolean} [reportNeedlessDisables]
  37. * @property {boolean} [reportInvalidScopeDisables]
  38. * @property {boolean} [reportDescriptionlessDisables]
  39. * @property {number} [maxWarnings]
  40. * @property {string | boolean} quiet
  41. * @property {string} [syntax]
  42. * @property {string} [version]
  43. * @property {boolean} [allowEmptyInput]
  44. */
  45. /**
  46. * @typedef {object} CLIOptions
  47. * @property {any} input
  48. * @property {any} help
  49. * @property {any} pkg
  50. * @property {Function} showHelp
  51. * @property {Function} showVersion
  52. * @property {CLIFlags} flags
  53. */
  54. /**
  55. * @typedef {object} OptionBaseType
  56. * @property {any} formatter
  57. * @property {boolean} [cache]
  58. * @property {string} [configFile]
  59. * @property {string} [cacheLocation]
  60. * @property {string} [customSyntax]
  61. * @property {string} [codeFilename]
  62. * @property {string} [configBasedir]
  63. * @property {{ quiet?: any }} configOverrides
  64. * @property {any} [printConfig]
  65. * @property {boolean} [fix]
  66. * @property {boolean} [ignoreDisables]
  67. * @property {any} [ignorePath]
  68. * @property {string} [outputFile]
  69. * @property {boolean} [reportNeedlessDisables]
  70. * @property {boolean} [reportInvalidScopeDisables]
  71. * @property {boolean} [reportDescriptionlessDisables]
  72. * @property {boolean} [disableDefaultIgnores]
  73. * @property {number} [maxWarnings]
  74. * @property {string} [syntax]
  75. * @property {string[]} [ignorePattern]
  76. * @property {boolean} [allowEmptyInput]
  77. * @property {string} [files]
  78. * @property {string} [code]
  79. */
  80. const meowOptions = {
  81. autoHelp: false,
  82. autoVersion: false,
  83. help: `
  84. Usage: stylelint [input] [options]
  85. Input: Files(s), glob(s), or nothing to use stdin.
  86. If an input argument is wrapped in quotation marks, it will be passed to
  87. globby for cross-platform glob support. node_modules are always ignored.
  88. You can also pass no input and use stdin, instead.
  89. Options:
  90. --config
  91. Path to a specific configuration file (JSON, YAML, or CommonJS), or the
  92. name of a module in node_modules that points to one. If no --config
  93. argument is provided, stylelint will search for configuration files in
  94. the following places, in this order:
  95. - a stylelint property in package.json
  96. - a .stylelintrc file (with or without filename extension:
  97. .json, .yaml, .yml, and .js are available)
  98. - a stylelint.config.js file exporting a JS object
  99. The search will begin in the working directory and move up the directory
  100. tree until a configuration file is found.
  101. --config-basedir
  102. An absolute path to the directory that relative paths defining "extends"
  103. and "plugins" are *relative to*. Only necessary if these values are
  104. relative paths.
  105. --print-config
  106. Print the configuration for the given path.
  107. --ignore-path, -i
  108. Path to a file containing patterns that describe files to ignore. The
  109. path can be absolute or relative to process.cwd(). By default, stylelint
  110. looks for .stylelintignore in process.cwd().
  111. --ignore-pattern, --ip
  112. Pattern of files to ignore (in addition to those in .stylelintignore)
  113. --syntax, -s
  114. Specify a syntax. Options: "css", "css-in-js", "html", "less",
  115. "markdown", "sass", "scss", "sugarss". If you do not specify a syntax,
  116. syntaxes will be automatically inferred by the file extensions
  117. and file content.
  118. --fix
  119. Automatically fix violations of certain rules.
  120. --custom-syntax
  121. Module name or path to a JS file exporting a PostCSS-compatible syntax.
  122. --stdin
  123. Accept stdin input even if it is empty.
  124. --stdin-filename
  125. A filename to assign stdin input.
  126. --ignore-disables, --id
  127. Ignore styleline-disable comments.
  128. --disable-default-ignores, --di
  129. Allow linting of node_modules.
  130. --cache [default: false]
  131. Store the info about processed files in order to only operate on the
  132. changed ones the next time you run stylelint. By default, the cache
  133. is stored in "./.stylelintcache". To adjust this, use --cache-location.
  134. --cache-location [default: '.stylelintcache']
  135. Path to a file or directory to be used for the cache location.
  136. Default is "./.stylelintcache". If a directory is specified, a cache
  137. file will be created inside the specified folder, with a name derived
  138. from a hash of the current working directory.
  139. If the directory for the cache does not exist, make sure you add a trailing "/"
  140. on *nix systems or "\\" on Windows. Otherwise the path will be assumed to be a file.
  141. --formatter, -f [default: "string"]
  142. The output formatter: ${getFormatterOptionsText({ useOr: true })}.
  143. --custom-formatter
  144. Path to a JS file exporting a custom formatting function.
  145. --quiet, -q
  146. Only register violations for rules with an "error"-level severity (ignore
  147. "warning"-level).
  148. --color
  149. --no-color
  150. Force enabling/disabling of color.
  151. --report-needless-disables, --rd
  152. Also report errors for stylelint-disable comments that are not blocking a lint warning.
  153. The process will exit with code ${EXIT_CODE_ERROR} if needless disables are found.
  154. --report-invalid-scope-disables, --risd
  155. Report stylelint-disable comments that used for rules that don't exist within the configuration object.
  156. The process will exit with code ${EXIT_CODE_ERROR} if invalid scope disables are found.
  157. --report-descriptionless-disables, --rdd
  158. Report stylelint-disable comments without a description.
  159. The process will exit with code ${EXIT_CODE_ERROR} if descriptionless disables are found.
  160. --max-warnings, --mw
  161. Number of warnings above which the process will exit with code ${EXIT_CODE_ERROR}.
  162. Useful when setting "defaultSeverity" to "warning" and expecting the
  163. process to fail on warnings (e.g. CI build).
  164. --output-file, -o
  165. Path of file to write report.
  166. --version, -v
  167. Show the currently installed version of stylelint.
  168. --allow-empty-input, --aei
  169. When glob pattern matches no files, the process will exit without throwing an error.
  170. `,
  171. flags: {
  172. allowEmptyInput: {
  173. alias: 'aei',
  174. type: 'boolean',
  175. },
  176. cache: {
  177. type: 'boolean',
  178. },
  179. cacheLocation: {
  180. type: 'string',
  181. },
  182. color: {
  183. type: 'boolean',
  184. },
  185. config: {
  186. type: 'string',
  187. },
  188. configBasedir: {
  189. type: 'string',
  190. },
  191. customFormatter: {
  192. type: 'string',
  193. },
  194. customSyntax: {
  195. type: 'string',
  196. },
  197. disableDefaultIgnores: {
  198. alias: 'di',
  199. type: 'boolean',
  200. },
  201. fix: {
  202. type: 'boolean',
  203. },
  204. formatter: {
  205. alias: 'f',
  206. default: 'string',
  207. type: 'string',
  208. },
  209. help: {
  210. alias: 'h',
  211. type: 'boolean',
  212. },
  213. ignoreDisables: {
  214. alias: 'id',
  215. type: 'boolean',
  216. },
  217. ignorePath: {
  218. alias: 'i',
  219. type: 'string',
  220. },
  221. ignorePattern: {
  222. alias: 'ip',
  223. type: 'string',
  224. isMultiple: true,
  225. },
  226. maxWarnings: {
  227. alias: 'mw',
  228. type: 'number',
  229. },
  230. outputFile: {
  231. alias: 'o',
  232. type: 'string',
  233. },
  234. printConfig: {
  235. type: 'boolean',
  236. },
  237. quiet: {
  238. alias: 'q',
  239. type: 'boolean',
  240. },
  241. reportDescriptionlessDisables: {
  242. alias: 'rdd',
  243. type: 'boolean',
  244. },
  245. reportInvalidScopeDisables: {
  246. alias: 'risd',
  247. type: 'boolean',
  248. },
  249. reportNeedlessDisables: {
  250. alias: 'rd',
  251. type: 'boolean',
  252. },
  253. stdin: {
  254. type: 'boolean',
  255. },
  256. stdinFilename: {
  257. type: 'string',
  258. },
  259. syntax: {
  260. alias: 's',
  261. type: 'string',
  262. },
  263. version: {
  264. alias: 'v',
  265. type: 'boolean',
  266. },
  267. },
  268. pkg: require('../package.json'),
  269. argv: /** @type {string[]} */ ([]),
  270. };
  271. /**
  272. * @param {string[]} argv
  273. * @returns {Promise<any>}
  274. */
  275. module.exports = (argv) => {
  276. const cli = buildCLI(argv);
  277. const invalidOptionsMessage = checkInvalidCLIOptions(meowOptions.flags, cli.flags);
  278. if (invalidOptionsMessage) {
  279. process.stderr.write(invalidOptionsMessage);
  280. process.exit(EXIT_CODE_ERROR); // eslint-disable-line no-process-exit
  281. }
  282. let formatter = cli.flags.formatter;
  283. if (cli.flags.customFormatter) {
  284. const customFormatter = path.isAbsolute(cli.flags.customFormatter)
  285. ? cli.flags.customFormatter
  286. : path.join(process.cwd(), cli.flags.customFormatter);
  287. formatter = require(customFormatter);
  288. }
  289. /** @type {OptionBaseType} */
  290. const optionsBase = {
  291. formatter,
  292. configOverrides: {},
  293. };
  294. if (cli.flags.quiet) {
  295. optionsBase.configOverrides.quiet = cli.flags.quiet;
  296. }
  297. if (cli.flags.syntax) {
  298. optionsBase.syntax = cli.flags.syntax;
  299. }
  300. if (cli.flags.customSyntax) {
  301. optionsBase.customSyntax = getModulePath(process.cwd(), cli.flags.customSyntax);
  302. }
  303. if (cli.flags.config) {
  304. // Should check these possibilities:
  305. // a. name of a node_module
  306. // b. absolute path
  307. // c. relative path relative to `process.cwd()`.
  308. // If none of the above work, we'll try a relative path starting
  309. // in `process.cwd()`.
  310. optionsBase.configFile =
  311. resolveFrom.silent(process.cwd(), cli.flags.config) ||
  312. path.join(process.cwd(), cli.flags.config);
  313. }
  314. if (cli.flags.configBasedir) {
  315. optionsBase.configBasedir = path.isAbsolute(cli.flags.configBasedir)
  316. ? cli.flags.configBasedir
  317. : path.resolve(process.cwd(), cli.flags.configBasedir);
  318. }
  319. if (cli.flags.stdinFilename) {
  320. optionsBase.codeFilename = cli.flags.stdinFilename;
  321. }
  322. if (cli.flags.ignorePath) {
  323. optionsBase.ignorePath = cli.flags.ignorePath;
  324. }
  325. if (cli.flags.ignorePattern) {
  326. optionsBase.ignorePattern = cli.flags.ignorePattern;
  327. }
  328. if (cli.flags.ignoreDisables) {
  329. optionsBase.ignoreDisables = cli.flags.ignoreDisables;
  330. }
  331. if (cli.flags.disableDefaultIgnores) {
  332. optionsBase.disableDefaultIgnores = cli.flags.disableDefaultIgnores;
  333. }
  334. if (cli.flags.cache) {
  335. optionsBase.cache = true;
  336. }
  337. if (cli.flags.cacheLocation) {
  338. optionsBase.cacheLocation = cli.flags.cacheLocation;
  339. }
  340. if (cli.flags.fix) {
  341. optionsBase.fix = cli.flags.fix;
  342. }
  343. if (cli.flags.outputFile) {
  344. optionsBase.outputFile = cli.flags.outputFile;
  345. }
  346. const reportNeedlessDisables = cli.flags.reportNeedlessDisables;
  347. const reportInvalidScopeDisables = cli.flags.reportInvalidScopeDisables;
  348. const reportDescriptionlessDisables = cli.flags.reportDescriptionlessDisables;
  349. if (reportNeedlessDisables) {
  350. optionsBase.reportNeedlessDisables = reportNeedlessDisables;
  351. }
  352. if (reportInvalidScopeDisables) {
  353. optionsBase.reportInvalidScopeDisables = reportInvalidScopeDisables;
  354. }
  355. if (reportDescriptionlessDisables) {
  356. optionsBase.reportDescriptionlessDisables = reportDescriptionlessDisables;
  357. }
  358. const maxWarnings = cli.flags.maxWarnings;
  359. if (maxWarnings !== undefined) {
  360. optionsBase.maxWarnings = maxWarnings;
  361. }
  362. if (cli.flags.help) {
  363. cli.showHelp(0);
  364. return Promise.resolve();
  365. }
  366. if (cli.flags.version) {
  367. cli.showVersion();
  368. return Promise.resolve();
  369. }
  370. if (cli.flags.allowEmptyInput) {
  371. optionsBase.allowEmptyInput = cli.flags.allowEmptyInput;
  372. }
  373. return Promise.resolve()
  374. .then(
  375. /**
  376. * @returns {Promise<OptionBaseType>}
  377. */
  378. () => {
  379. // Add input/code into options
  380. if (cli.input.length) {
  381. return Promise.resolve({ ...optionsBase, files: /** @type {string} */ (cli.input) });
  382. }
  383. return getStdin().then((stdin) => ({ ...optionsBase, code: stdin }));
  384. },
  385. )
  386. .then((options) => {
  387. if (cli.flags.printConfig) {
  388. return printConfig(options)
  389. .then((config) => {
  390. process.stdout.write(JSON.stringify(config, null, ' '));
  391. })
  392. .catch(handleError);
  393. }
  394. if (!options.files && !options.code && !cli.flags.stdin) {
  395. cli.showHelp();
  396. return;
  397. }
  398. return standalone(options)
  399. .then((linted) => {
  400. if (!linted.output) {
  401. return;
  402. }
  403. process.stdout.write(linted.output);
  404. if (options.outputFile) {
  405. writeOutputFile(linted.output, options.outputFile).catch(handleError);
  406. }
  407. if (linted.errored) {
  408. process.exitCode = EXIT_CODE_ERROR;
  409. } else if (maxWarnings !== undefined && linted.maxWarningsExceeded) {
  410. const foundWarnings = linted.maxWarningsExceeded.foundWarnings;
  411. process.stderr.write(
  412. `${EOL}${chalk.red(`Max warnings exceeded: `)}${foundWarnings} found. ${chalk.dim(
  413. `${maxWarnings} allowed${EOL}${EOL}`,
  414. )}`,
  415. );
  416. process.exitCode = EXIT_CODE_ERROR;
  417. }
  418. })
  419. .catch(handleError);
  420. });
  421. };
  422. /**
  423. * @param {{ stack: any, code: any }} err
  424. * @returns {void}
  425. */
  426. function handleError(err) {
  427. process.stderr.write(err.stack + EOL);
  428. const exitCode = typeof err.code === 'number' ? err.code : 1;
  429. process.exitCode = exitCode;
  430. }
  431. /**
  432. * @param {string[]} argv
  433. * @returns {CLIOptions}
  434. */
  435. function buildCLI(argv) {
  436. // @ts-ignore TODO TYPES
  437. return meow({ ...meowOptions, argv });
  438. }
  439. module.exports.buildCLI = buildCLI;