UNPKG

ember-cli

Version:

Command line tool for developing ambitious ember.js apps

269 lines (234 loc) 9.96 kB
/* This utility processes the argument passed with the `lang` option in ember-cli, i.e. `ember (new||init||addon) app-name --lang=langArg` Execution Context (usage, input, output, error handling, etc.): - called directly by `init` IFF `--lang` flag is used in (new||init||addon) - receives single input: the argument passed with `lang` (herein `langArg`) - processes `langArg`: lang code validation + error detection / handling - DOES emit Warning messages if necessary - DOES NOT halt execution process / throw errors / disrupt the build - returns single result as output (to `init`): - `langArg` (if it is a valid language code) - `undefined` (otherwise) - `init` assigns the value of `commandOptions.lang` to the returned result - downstream, the `lang` attribute is assigned via inline template control: - file: `blueprints/app/files/app/index.html` - logic: `<html<% if(lang) { %> lang="<%= lang %>"<% } %>> Internal Mechanics -- the utility processes `langArg` to determine: - the value to return to `init` (i.e. validated lang code or undefined) - a descriptive category for the usage: `correct`, `incorrect`, `edge`, etc. - what message text (if any: category-dependent) to emit before return Warning Messages (if necessary): - An internal instance of `console-ui` is used to emit messages - IFF there is a message, it will be emitted before returning the result - Components of all emitted messages -- [Name] (writeLevel): 'example': - [`HEAD`] (WARNING): 'A warning was generated while processing `--lang`:' - [`BODY`] (WARNING): 'Invalid language code, `en-UK`' - [`STATUS`] (WARNING): '`lang` will NOT be set to `en-UK` in `index.html`' - [`HELP`] (INFO): 'Correct usage of `--lang`: ... ' */ 'use strict'; const { isLangCode } = require('is-language-code'); // Primary language code validation function (boolean) function isValidLangCode(langArg) { return isLangCode(langArg).res; } // Generates the result to pass back to `init` function getResult(langArg) { return isValidLangCode(langArg) ? langArg : undefined; } /* Misuse case: attempt to set application programming language via `lang` AND Edge case: valid language code AND a common programming language abbreviation ------------------------------------------------------------------------------- It is possible that a user might mis-interpret the type of `language` that is specified by the `--lang` flag. One notable potential `misuse case` is one in which the user thinks `--lang` specifies the application's programming language. For example, the user might call `ember new my-app --lang=typescript` expecting to achieve an effect similar to the one provided by the `ember-cli-typescript` addon. This misuse case is handled by checking the input `langArg` against an Array containing notable programming language-related values: language names (e.g. `JavaScript`), abbreviations (e.g. `js`), file extensions (e.g. `.js`), or versions (e.g. `ES6`), etc. Specifically, if `langArg` is found within this reference list, a WARNING message that describes correct `--lang` usage will be emitted. The `lang` attribute will not be assigned in `index.html`, and the user will be notified with a corresponding STATUS message. There are several edge cases (marked) where `langArg` is both a commonly-used abbreviation for a programming language AND a valid language code. The behavior for these cases is to assume the user has used `--lang` correctly and set the `lang` attribute to the valid code in `index.html`. To cover for potential misuage, several helpful messages will also be emitted: - `ts` is a valid language code AND a common programming language abbreviation - the `lang` attribute will be set to `ts` in the application - if this is not correct, it can be changed in `app/index.html` directly - (general `help` information about correct `--lang` usage) */ const PROG_LANGS = [ 'javascript', '.js', 'js', 'emcascript2015', 'emcascript6', 'es6', 'emcascript2016', 'emcascript7', 'es7', 'emcascript2017', 'emcascript8', 'es8', 'emcascript2018', 'emcascript9', 'es9', 'emcascript2019', 'emcascript10', 'es10', 'typescript', '.ts', 'node.js', 'node', 'handlebars', '.hbs', 'hbs', 'glimmer', 'glimmer.js', 'glimmer-vm', 'markdown', 'markup', 'html5', 'html4', '.md', '.html', '.htm', '.xhtml', '.xml', '.xht', 'md', 'html', 'htm', 'xhtml', '.sass', '.scss', '.css', 'sass', 'scss', // Edge Cases 'ts', // Tsonga 'TS', // Tsonga (case insensitivity check) 'xml', // Malaysian Sign Language 'xht', // Hattic 'css', // Costanoan ]; function isProgLang(langArg) { return langArg && PROG_LANGS.includes(langArg.toLowerCase().trim()); } function isValidCodeAndProg(langArg) { return isValidLangCode(langArg) && isProgLang(langArg); } /* Misuse case: `--lang` called without `langArg` (NB: parser bug workaround) ------------------------------------------------------------------------------- This is a workaround for handling an existing bug in the ember-cli parser where the `--lang` option is specified in the command without a corresponding value for `langArg`. As examples, the parser behavior would likely affect the following usages: 1. `ember new app-name --lang --skip-npm 2. `ember new app-name --lang` In this context, the unintended parser behavior is that `langArg` will be assigned to the String that immediately follows `--lang` in the command. If `--lang` is the last explicitly defined part of the command (as in the second example above), the first of any any `hidden` options pushed onto the command after the initial parse (e.g. `--no-watcher`) will be used when assigning `langArg`. In the above examples, `langArg` would likely be assigned as follows: 1. `ember new app-name --lang --skip-npm => `langArg='--skip-npm'` The workaround implemented herein is to check whether or not the value of `langArg` starts with a hyphen. The rationale for this approach is based on the following underlying assumptions: - ALL CLI options start with (at least one) hyphen - NO valid language codes start with a hyphen If the leading hyphen is detected, the current behavior is to assume `--lang` was declared without a corresponding specification. A WARNING message that describes correct `--lang` usage will be emitted. The `lang` attribute will not be assigned in `index.html`, and the user will be notified with a corresponding STATUS message. Execution will not be halted. Other complications related to this parser behavior are considered out-of-scope and not handled here. In the first example above, this workaround would ensure that `lang` is not assigned to `--skip-npm`, but it would not guarantee that `--skip-npm` is correctly processed as a command option. That is, `npm` may or may not get skipped during execution. */ function startsWithHyphen(langArg) { return langArg && langArg[0] === '-'; } // MESSAGE GENERATION: // 1. `HEAD` Message: template for all `--lang`-related warnings emitted const MSG_HEAD = `An issue with the \`--lang\` flag returned the following message:`; // 2. `BODY` Messages: category-dependent context information // Message context from language code validation (valid: null, invalid: reason) function getLangCodeMsg(langArg) { return isLangCode(langArg).message; } // Edge case: valid language code AND a common programming language abbreviation function getValidAndProgMsg(langArg) { return `The \`--lang\` flag has been used with argument \`${langArg}\`, which is BOTH a valid language code AND an abbreviation for a programming language. ${getProgLangMsg(langArg)}`; } // Misuse case: attempt to set application programming language via `lang` function getProgLangMsg(langArg) { return `Trying to set the app programming language to \`${langArg}\`? This is not the intended usage of the \`--lang\` flag.`; } // Misuse case: `--lang` called without `langArg` (NB: parser bug workaround) function getCliMsg() { return `Detected a \`--lang\` specification starting with command flag \`-\`. This issue is likely caused by using the \`--lang\` flag without a specification.`; } // 3. `STATUS` message: report if `lang` will be set in `index.html` function getStatusMsg(langArg, willSet) { return `The human language of the application will ${willSet ? `be set to ${langArg}` : `NOT be set`} in the \`<html>\` element's \`lang\` attribute in \`index.html\`.`; } // 4. `HELP` message: template for all `--lang`-related warnings emitted const MSG_HELP = `If this was not your intention, you may edit the \`<html>\` element's \`lang\` attribute in \`index.html\` manually after the process is complete. Information about using the \`--lang\` flag: The \`--lang\` flag sets the base human language of an app or test app: - \`app/index.html\` (app) - \`tests/dummy/app/index.html\` (addon test app) If used, the lang option must specfify a valid language code. For default behavior, remove the flag. See \`ember <command> help\` for more information.`; function getBodyMsg(langArg) { return isValidCodeAndProg(langArg) ? getValidAndProgMsg(langArg) : isProgLang(langArg) ? getProgLangMsg(langArg) : startsWithHyphen(langArg) ? getCliMsg(langArg) : getLangCodeMsg(langArg); } function getFullMsg(langArg) { return { head: MSG_HEAD, body: getBodyMsg(langArg), status: getStatusMsg(langArg, isValidCodeAndProg(langArg)), help: MSG_HELP, }; } function writeFullMsg(fullMsg, ui) { ui.setWriteLevel('WARNING'); ui.writeWarnLine(`${fullMsg.head}\n ${fullMsg.body}\``); ui.writeWarnLine(fullMsg.status); ui.setWriteLevel('INFO'); ui.writeInfoLine(fullMsg.help); } module.exports = function getLangArg(langArg, ui) { let fullMsg = getFullMsg(langArg); if (fullMsg.body) { writeFullMsg(fullMsg, ui); } return getResult(langArg); };