quasar-app-extension-qautomate
Advanced tools
Comparing version 1.0.0-alpha.2 to 1.0.0-alpha.3
{ | ||
"name": "quasar-app-extension-qautomate", | ||
"version": "1.0.0-alpha.2", | ||
"description": "Automatically add components, directives and plugins to quasar.conf.js", | ||
"version": "1.0.0-alpha.3", | ||
"description": "Automatically add missing Quasar components, directives and plugins to quasar.conf.js", | ||
"author": "webnoob <allangaunt@gmail.com>", | ||
@@ -36,3 +36,5 @@ "license": "MIT", | ||
"dependencies": { | ||
"chalk": "^2.4.2", | ||
"fs-extra": "^8.1.0", | ||
"inquirer": "^6.4.1", | ||
"param-case": "^2.1.1", | ||
@@ -39,0 +41,0 @@ "ware-loader": "^0.2.4" |
@@ -25,6 +25,5 @@ QAutomate | ||
(yet to be implemented): | ||
**Mode** [Automatic | Manual | Warn] - How QAutomate should behave when missing items are detected. | ||
1. AutoFix - Whether or not the AE should auto add to the Quasar conf or just notify in console. | ||
2. Manual Mode - Show a list of missing components / directives / plugins and allow you to select which ones to add. | ||
**Sort** [True | False] - Should QAutomate sort the items in components, directives or plugins? | ||
@@ -48,3 +47,3 @@ # Uninstall | ||
# Patreon | ||
# Support Us | ||
If you like (and use) this App Extension, please consider becoming a Quasar [GH Sponsor](https://donate.quasar.dev). |
114
src/index.js
@@ -9,4 +9,104 @@ /** | ||
const QAutomate = require('./lib/QAutomate') | ||
const chalk = require('chalk') | ||
const inquirer = require('inquirer') | ||
let qAutomate = null | ||
class QAutomatePlugin { | ||
constructor (api) { | ||
this.quasarConfFixMode = api.prompts.quasarConfFixMode | ||
} | ||
apply(compiler) { | ||
compiler.hooks.done.tap('done-compiling', async () => { | ||
switch (this.quasarConfFixMode) { | ||
case 'automatic': | ||
// Needs a rework to get this working as the quasar.conf.js save on auto triggers a complete reload | ||
// and we lose the stored value in our qAutomate class. | ||
printSummary('bgGreen', 'Automatically added the following:') | ||
qAutomate.applyChanges() | ||
break | ||
case 'warn': | ||
printSummary('bgRed', 'Found missing items') | ||
break | ||
case 'manual': | ||
await presentManualOptions() | ||
break | ||
} | ||
// Write the buffer to console (gets cleared on HMR so handled internally) | ||
qAutomate.writeLog() | ||
// Reset the qAutomate instance for the next scan. | ||
qAutomate.reset() | ||
}) | ||
} | ||
} | ||
const presentManualOptions = () => { | ||
return new Promise(resolve => { | ||
const missingItems = qAutomate.getAnalysisResult().missing | ||
let choices = [] | ||
for (let group in missingItems) { | ||
choices.push(new inquirer.Separator(chalk` | ||
{bgWhite {black ${group}}} | ||
`)) | ||
for (let item of missingItems[group]) { | ||
choices.push({ | ||
name: item | ||
}) | ||
} | ||
} | ||
inquirer.prompt([ | ||
{ | ||
type: 'checkbox', | ||
message: chalk`{bgGreen Select items to add to quasar.conf.js}`, | ||
name: 'items', | ||
choices: choices, | ||
prefix: '', | ||
pageSize: 20 | ||
} | ||
]).then(answers => { | ||
qAutomate.applyChanges(answers.items) | ||
resolve() | ||
}) | ||
console.log(` | ||
`) // To add a line before the dev server message. | ||
}) | ||
} | ||
const printSummary = (colorStyle, msg) => { | ||
const missingItems = qAutomate.getAnalysisResult().missing | ||
if (Object.keys(missingItems).length > 0) { | ||
qAutomate.addLog(chalk`{bold {${colorStyle} App Extension (qautomate) Info: ${msg}}}`) | ||
} | ||
for (let group in missingItems) { | ||
qAutomate.addLog(chalk` | ||
{bold {green ${group}}}` | ||
) | ||
for (let item of missingItems[group]) { | ||
qAutomate.addLog(` ${item}`) | ||
} | ||
} | ||
qAutomate.addLog(` | ||
`) | ||
} | ||
const handlePromptType = (src, prompts) => { | ||
switch (prompts.quasarConfFixMode) { | ||
case 'automatic': | ||
case 'manual': | ||
default: | ||
qAutomate.analyse(src) | ||
return src | ||
} | ||
} | ||
/** | ||
@@ -19,3 +119,3 @@ * Setup a loader that will allow us to hook into file changes and get the source code. | ||
const chainWebpack = function (api, chain, { isClient }) { | ||
console.log(` App Extension (qautomate) Info: Hooking up loader for file component checks.`) | ||
console.log(chalk` {green app:extension} Extension(qautomate): Hooking up loader for file component checks.`) | ||
const rule = chain.module.rule('qautomate-vue') | ||
@@ -31,3 +131,3 @@ .test(/\.(js|vue)$/) | ||
raw: true, | ||
middleware: src => qAutomate.run(src) | ||
middleware: src => handlePromptType(src, api.prompts) | ||
}) | ||
@@ -37,4 +137,12 @@ } | ||
module.exports = function (api) { | ||
qAutomate = new QAutomate(api) | ||
qAutomate = new QAutomate(api, { | ||
sort: api.prompts.sort | ||
}) | ||
api.chainWebpack((chain, { isClient }) => chainWebpack(api, chain, isClient)) | ||
// Register a plugin with a hook for logging etc. | ||
api.extendWebpack((cfg, { isClient, isServer }, api) => { | ||
cfg.plugins.push(new QAutomatePlugin(api)) | ||
}) | ||
} |
@@ -6,8 +6,31 @@ const path = require('path') | ||
module.exports = class QAutomate { | ||
constructor (api) { | ||
constructor (api, options) { | ||
this._api = api | ||
this._whitelists = {} | ||
this._quasarConfPath = path.join(this._api.appDir, 'quasar.conf.js') | ||
this._quasarConf = require(this._quasarConfPath)(this._api.ctx) | ||
this._quasarConfFileData = fs.readFileSync(this._quasarConfPath, 'utf8') | ||
this._originalQuasarConfFileData = this._quasarConfFileData | ||
this._options = options | ||
this._forceApplyChanges = false | ||
this.reset() | ||
this.buildWhitelist() | ||
} | ||
reset () { | ||
this._whitelists = {} | ||
this._analysis = { | ||
existing: {}, | ||
missing: {}, | ||
merged: {} | ||
} | ||
this.clearLog() | ||
// If we're requiring changes to be saved, make sure they are before the reset happens. | ||
if (this._forceApplyChanges) { | ||
this.applyChanges() | ||
} | ||
} | ||
/** | ||
@@ -38,8 +61,7 @@ * Find the quasar package installed and build an allowed list of tags we can look for based on the API.json files. | ||
* Scan the source template and get all the Quasar items | ||
* NOTE: This is currently HTML only and won't support PUG. | ||
* @param group | ||
* @param source | ||
* @param group - The type we're looking for i.e Components, Directives or Plugins. | ||
* @param source - HTML / PUG / Text to scan | ||
* @returns {string[]} | ||
*/ | ||
getSourceItems (group, source) { | ||
scanForSourceItems (group, source) { | ||
const result = [] | ||
@@ -67,28 +89,4 @@ for (let wlItem of this._whitelists[group]) { | ||
/** | ||
* Receive a list of items for a given group to check and return a list of items that need to be added. | ||
* @param group - i.e components, directives, plugins | ||
* @param quasarConf - The actual conf file | ||
* @param itemsInUseInSource - The current list of {group} items in use within the source file. | ||
* @param quasarConfFileData - The string of file data from quasarconf file. | ||
* @returns {*} | ||
*/ | ||
getMissingGroupItems (group, quasarConf, itemsInUseInSource, quasarConfFileData) { | ||
const missingItems = itemsInUseInSource.filter(e => !quasarConf.framework[group].includes(e)) | ||
if (missingItems.length > 0) { | ||
let currentItems = this.getFrameworkGroup(group, quasarConfFileData) | ||
for (let missingItem of missingItems) { | ||
if (currentItems[group].indexOf(missingItem) === -1) { | ||
console.log(` App Extension (qautomate) Info: Adding missing [${group}] to quasar.conf.js [${missingItem}]`) | ||
currentItems[group].push(missingItem) | ||
} | ||
} | ||
return currentItems | ||
} | ||
return itemsInUseInSource | ||
} | ||
/** | ||
* Convert a list of items back into a format suitable for the quasar.conf.js file. | ||
* TODO: There has to be a better way of doing this - quasar.conf.js is a function file so have parsed manually. | ||
* @param group | ||
@@ -98,4 +96,4 @@ * @param data | ||
*/ | ||
stringifyConf (group, data) { | ||
return JSON.stringify(data) | ||
stringifyConfGroup (group, items) { | ||
return JSON.stringify( { [group] : items }) | ||
.replace('{', '') | ||
@@ -121,51 +119,128 @@ .replace('}', '') | ||
/** | ||
* Get a group section i.e `components: [ 'QInput' ]` from | ||
* the quasar.conf.js file and return the array for processing. | ||
* @param group | ||
* @param fileData | ||
* @returns {any} | ||
* Analyse the source for missing items (items based on the type set in Quasar's api.json files) | ||
* Eg: Components, Directives and Plugins | ||
* @param source | ||
* @returns {{}|*} | ||
*/ | ||
getFrameworkGroup (group, fileData) { | ||
let componentsGroup = fileData.match(this.getGroupRegex(group))[0] | ||
componentsGroup = `{ | ||
${componentsGroup | ||
.replace(group, `"${group}"`) | ||
.replace(/'/g, '"') | ||
analyse (source) { | ||
// Loop through our groups we want to check and process them. | ||
for (let group in this._whitelists) { | ||
const sourceItems = this.scanForSourceItems(group, source) | ||
// Build an array of missing items, grouped by type (i.e component, plugin, directive) | ||
// without duplicates also taking into account the group might not have been initialised | ||
// as an array. | ||
const missingItems = sourceItems.filter(e => | ||
!this._quasarConf.framework[group].includes(e) && | ||
!(this._analysis.missing[group] || []).includes(e) | ||
) | ||
if (missingItems.length > 0) { | ||
this._analysis.missing[group] = this.mergeArrays(this._analysis.missing[group], missingItems) | ||
} | ||
}` | ||
return JSON.parse(componentsGroup) | ||
this._analysis.existing[group] = this._quasarConf.framework[group] | ||
this._analysis.merged[group] = this.mergeArrays(this._analysis.existing[group], this._analysis.missing[group]) | ||
// If sorting is enabled, check to see if we need to apply sorting. | ||
// Then flag to force a save if reset is called before applyChanges() | ||
if (this._options.sort) { | ||
this._analysis.merged[group].sort() | ||
// Using stringify here as as I know the data will always be an array. | ||
if (JSON.stringify(this._analysis.existing[group]) !== JSON.stringify(this._analysis.merged[group])) { | ||
this._forceApplyChanges = true | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Find any components, directives or plugins in use in the source code that aren't contained | ||
* in the quasar.conf.js file and add them in. | ||
* @param source | ||
* @param api | ||
* Apply all the analysis results that have been run. | ||
* @param analysisResult | ||
* @returns {*} | ||
*/ | ||
run (source) { | ||
const | ||
quasarConfPath = path.join(this._api.appDir, 'quasar.conf.js'), | ||
quasarConf = require(quasarConfPath)(this._api.ctx), | ||
quasarConfFileData = fs.readFileSync(quasarConfPath, 'utf8') | ||
let newData = quasarConfFileData | ||
// Loop through our groups we want to check and process them. | ||
for (let groupKey in this._whitelists) { | ||
const sourceItems = this.getSourceItems(groupKey, source) | ||
let newGroupItemList = this.getMissingGroupItems(groupKey, quasarConf, sourceItems, newData) | ||
if (newGroupItemList.length !== sourceItems.length) { | ||
newData = newData.replace(this.getGroupRegex(groupKey), this.stringifyConf(groupKey, newGroupItemList)) | ||
applyChanges (selectedItems = []) { | ||
for (let group in this._analysis.merged) { | ||
// Pick out only the selected items for this group or add them all based on selectedItems | ||
let itemsToReplace = selectedItems.length > 0 | ||
? this.mergeArrays(this._analysis.existing[group], this._analysis.missing[group], f => selectedItems.includes(f)) | ||
: this._analysis.merged[group] | ||
// Call sort again as the manual items may have thrown off sorting. | ||
if (this._options.sort) { | ||
itemsToReplace.sort() | ||
} | ||
if (itemsToReplace !== void 0) { | ||
this._quasarConfFileData = this._quasarConfFileData.replace(this.getGroupRegex(group), this.stringifyConfGroup(group, itemsToReplace)) | ||
} | ||
} | ||
if (newData !== quasarConfFileData) { | ||
fs.writeFileSync(quasarConfPath, newData, 'utf8') | ||
if (this._quasarConfFileData !== this._originalQuasarConfFileData) { | ||
this._originalQuasarConfFileData = this._quasarConfFileData | ||
fs.writeFileSync(this._quasarConfPath, this._quasarConfFileData, 'utf8') | ||
} | ||
this._forceApplyChanges = false | ||
} | ||
/** | ||
* Simple helper to merge 2 arrays with filter even if arrays aren't initialised. | ||
* @param arr1 | ||
* @param arr2 | ||
* @param filterFn | ||
* @returns {*[]} | ||
*/ | ||
mergeArrays (arr1, arr2, filterFn) { | ||
const arr = arr2 || [] | ||
const filteredArray = typeof filterFn === 'function' | ||
? arr.filter(filterFn) | ||
: arr | ||
return [].concat.apply([], [arr1 || [], filteredArray]) | ||
} | ||
return source | ||
/** | ||
* this.analyse() the source and then this.applyChanges() | ||
* @param source | ||
* @returns {*} | ||
*/ | ||
analyseAndApply (source) { | ||
this.analyse(source) | ||
return this.applyChanges() | ||
} | ||
/** | ||
* External access to the accumulative scan result. | ||
* @returns {{existing: {}, missing: {}, merged: {}}|*} | ||
*/ | ||
getAnalysisResult () { | ||
return this._analysis | ||
} | ||
/** | ||
* HMR console logging will be cleared so maintain an internal version | ||
* @param msg | ||
*/ | ||
addLog (msg) { | ||
this._logs.push(msg) | ||
} | ||
/** | ||
* Clear current internal log buffer | ||
*/ | ||
clearLog () { | ||
this._logs = [] | ||
} | ||
/** | ||
* Write the current log buffer to console | ||
* To be used when HMR has finished to avoid | ||
*/ | ||
writeLog () { | ||
for (let log of this._logs) { | ||
console.log(log) | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
15213
5
368
5
48
1
+ Addedchalk@^2.4.2
+ Addedinquirer@^6.4.1
+ Addedansi-escapes@3.2.0(transitive)
+ Addedansi-regex@3.0.14.1.1(transitive)
+ Addedansi-styles@3.2.1(transitive)
+ Addedchalk@2.4.2(transitive)
+ Addedchardet@0.7.0(transitive)
+ Addedcli-cursor@2.1.0(transitive)
+ Addedcli-width@2.2.1(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedexternal-editor@3.1.0(transitive)
+ Addedfigures@2.0.0(transitive)
+ Addedhas-flag@3.0.0(transitive)
+ Addediconv-lite@0.4.24(transitive)
+ Addedinquirer@6.5.2(transitive)
+ Addedis-fullwidth-code-point@2.0.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmimic-fn@1.2.0(transitive)
+ Addedmute-stream@0.0.7(transitive)
+ Addedonetime@2.0.1(transitive)
+ Addedos-tmpdir@1.0.2(transitive)
+ Addedrestore-cursor@2.0.0(transitive)
+ Addedrun-async@2.4.1(transitive)
+ Addedrxjs@6.6.7(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedsignal-exit@3.0.7(transitive)
+ Addedstring-width@2.1.1(transitive)
+ Addedstrip-ansi@4.0.05.2.0(transitive)
+ Addedsupports-color@5.5.0(transitive)
+ Addedthrough@2.3.8(transitive)
+ Addedtmp@0.0.33(transitive)
+ Addedtslib@1.14.1(transitive)