-
-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `au config` command gets or sets configuration for the Aurelia application. `au config <key> <value> --get --set --clear --add --remove --no-save --no-backup` Closes #629.
- Loading branch information
Showing
10 changed files
with
557 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
'use strict'; | ||
|
||
const UI = require('../../ui').UI; | ||
const CLIOptions = require('../../cli-options').CLIOptions; | ||
const Container = require('aurelia-dependency-injection').Container; | ||
const os = require('os'); | ||
|
||
const Configuration = require('./configuration'); | ||
const ConfigurationUtilities = require('./util'); | ||
|
||
module.exports = class { | ||
static inject() { return [Container, UI, CLIOptions]; } | ||
|
||
constructor(container, ui, options) { | ||
this.container = container; | ||
this.ui = ui; | ||
this.options = options; | ||
} | ||
|
||
execute(args) { | ||
this.config = new Configuration(this.options); | ||
this.util = new ConfigurationUtilities(this.options, args); | ||
let key = this.util.getArg(0) || ''; | ||
let value = this.util.getValue(this.util.getArg(1)); | ||
let save = !CLIOptions.hasFlag('no-save'); | ||
let backup = !CLIOptions.hasFlag('no-backup'); | ||
let action = this.util.getAction(value); | ||
|
||
this.displayInfo(`Performing configuration action '${action}' on '${key}'`, (value ? `with '${value}'` : '')); | ||
this.displayInfo(this.config.execute(action, key, value)); | ||
|
||
if (action !== 'get') { | ||
if (save) { | ||
this.config.save(backup).then((name) => { | ||
this.displayInfo('Configuration saved. ' + (backup ? `Backup file '${name}' created.` : 'No backup file was created.')); | ||
}); | ||
} | ||
else { | ||
this.displayInfo(`Action was '${action}', but no save was performed!`); | ||
} | ||
} | ||
} | ||
|
||
displayInfo(message) { | ||
return this.ui.log(message + os.EOL); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ | ||
"name": "config", | ||
"description": "Gets or sets configuration for the Aurelia application.", | ||
"parameters": [ | ||
{ | ||
"name": "key", | ||
"optional": true, | ||
"description": "The key you want to get or set. Supports hierarchies and array indexes, for example build.targets[0] and arrayWithArray[2].[1]" | ||
}, | ||
{ | ||
"name": "value", | ||
"optional": true, | ||
"description": "The value you want to set the key to. Supports json, for example \"{ \\\"myKey\\\": \\\"myValue\\\" }\"" | ||
} | ||
], | ||
"flags": [ | ||
{ | ||
"name": "get", | ||
"description": "Gets the content of key, ignoring value parameter (the same as not specifying a value).", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "set", | ||
"description": "Sets the content of key to value, replacing any existing content.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "clear", | ||
"description": "Deletes the key and all its content from the configuration.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "add", | ||
"description": "If value or existing content of the key is an array, adds value(s) to existing content. If value is an object, merges it into existing content of key.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "remove", | ||
"description": "If value or existing content of the key is an array, removes value(s) from existing content. If value or existing content of the key is an object, removes key(s) from existing content of key.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "no-save", | ||
"description": "Don't save the changes in the configuration file.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "no-backup", | ||
"description": "Don't create a backup configuration file before saving changes.", | ||
"type": "boolean" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
'use strict'; | ||
|
||
const os = require('os'); | ||
const copySync = require('../../file-system').copySync; | ||
const readFileSync = require('../../file-system').readFileSync; | ||
const writeFile = require('../../file-system').writeFile; | ||
|
||
class Configuration { | ||
constructor(options) { | ||
this.options = options; | ||
this.aureliaJsonPath = options.originalBaseDir + '/aurelia_project/aurelia.json'; | ||
this.project = JSON.parse(readFileSync(this.aureliaJsonPath)); | ||
} | ||
|
||
configEntry(key, createKey) { | ||
let entry = this.project; | ||
let keys = key.split('.'); | ||
|
||
if (!keys[0]) { | ||
return entry; | ||
} | ||
|
||
while (entry && keys.length) { | ||
key = this.parsedKey(keys.shift()); | ||
if (entry[key.value] === undefined || entry[key.value] === null) { | ||
if (!createKey) { | ||
return entry[key.value]; | ||
} | ||
let checkKey = this.parsedKey(keys.length ? keys[0] : createKey); | ||
if (checkKey.index) { | ||
entry[key.value] = []; | ||
} | ||
else if (checkKey.key) { | ||
entry[key.value] = {}; | ||
} | ||
} | ||
entry = entry[key.value]; | ||
|
||
// TODO: Add support for finding objects based on input values? | ||
// TODO: Add support for finding string in array? | ||
} | ||
|
||
return entry; | ||
} | ||
|
||
parsedKey(key) { | ||
if (/\[(\d+)\]/.test(key)) { | ||
return { index: true, key: false, value: +(RegExp.$1) }; | ||
} | ||
else { | ||
return { index: false, key: true, value: key }; | ||
} | ||
} | ||
|
||
normalizeKey(key) { | ||
const re = /([^.])\[/; | ||
while (re.exec(key)) { | ||
key = key.replace(re, RegExp.$1 + '.['); | ||
} | ||
|
||
let keys = key.split('.'); | ||
for (let i = 0; i < keys.length; i++) { | ||
if (/\[(\d+)\]/.test(keys[i])) { | ||
// console.log(`keys[${i}] is index: ${keys[i]}`); | ||
} | ||
else if (/\[(.+)\]/.test(keys[i])) { | ||
// console.log(`keys[${i}] is indexed name: ${keys[i]}`); | ||
keys[i] = RegExp.$1; | ||
} | ||
else { | ||
// console.log(`keys[${i}] is name: ${keys[i]}`); | ||
} | ||
} | ||
|
||
return keys.join('.'); | ||
} | ||
|
||
execute(action, key, value) { | ||
let originalKey = key; | ||
|
||
key = this.normalizeKey(key); | ||
|
||
if (action === 'get') { | ||
return `Configuration key '${key}' is:` + os.EOL + JSON.stringify(this.configEntry(key), null, 2); | ||
} | ||
|
||
let keys = key.split('.'); | ||
key = this.parsedKey(keys.pop()); | ||
let parent = keys.join('.'); | ||
|
||
if (action === 'set') { | ||
let entry = this.configEntry(parent, key.value); | ||
if (entry) { | ||
entry[key.value] = value; | ||
} | ||
else { | ||
console.log('Failed to set property', this.normalizeKey(originalKey), '!'); | ||
} | ||
} | ||
else if (action === 'clear') { | ||
let entry = this.configEntry(parent); | ||
if (entry && (key.value in entry)) { | ||
delete entry[key.value]; | ||
} | ||
else { | ||
console.log('No property', this.normalizeKey(originalKey), 'to clear!'); | ||
} | ||
} | ||
else if (action === 'add') { | ||
let entry = this.configEntry(parent, key.value); | ||
if (Array.isArray(entry[key.value]) && !Array.isArray(value)) { | ||
value = [value]; | ||
} | ||
if (Array.isArray(value) && !Array.isArray(entry[key.value])) { | ||
entry[key.value] = (entry ? [entry[key.value]] : []); | ||
} | ||
if (Array.isArray(value)) { | ||
entry[key.value].push(...value); | ||
} | ||
else if (Object(value) === value) { | ||
if (Object(entry[key.value]) !== entry[key.value]) { | ||
entry[key.value] = {}; | ||
} | ||
Object.assign(entry[key.value], value); | ||
} | ||
else { | ||
entry[key.value] = value; | ||
} | ||
} | ||
else if (action === 'remove') { | ||
let entry = this.configEntry(parent); | ||
|
||
if (Array.isArray(entry) && key.index) { | ||
entry.splice(key.value, 1); | ||
} | ||
else if (Object(entry) === entry && key.key) { | ||
delete entry[key.value]; | ||
} | ||
else if (!entry) { | ||
console.log('No property', this.normalizeKey(originalKey), 'to remove from!'); | ||
} | ||
else { | ||
console.log("Can't remove value from", entry[key.value], "!"); | ||
} | ||
} | ||
key = this.normalizeKey(originalKey); | ||
return `Configuration key '${key}' is now:` + os.EOL + JSON.stringify(this.configEntry(key), null, 2); | ||
} | ||
|
||
save(backup = true) { | ||
const unique = new Date().toISOString().replace(/[T\D]/g, ''); | ||
let arr = this.aureliaJsonPath.split(/[\\\/]/); | ||
const name = arr.pop(); | ||
const path = arr.join('/'); | ||
const bak = `${name}.${unique}.bak`; | ||
|
||
if (backup) { | ||
copySync(this.aureliaJsonPath, [path, bak].join('/')); | ||
} | ||
return writeFile(this.aureliaJsonPath, JSON.stringify(this.project, null, 2), 'utf8') | ||
.then(() => { return bak }); | ||
} | ||
} | ||
|
||
module.exports = Configuration; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
'use strict'; | ||
|
||
class ConfigurationUtilities { | ||
constructor(options, args) { | ||
this.options = options; | ||
this.args = args; | ||
} | ||
|
||
getArg(arg) { | ||
let args = this.args; | ||
if (args) { | ||
for (let i = 0; i < args.length; i++) { | ||
if (args[i].startsWith('--')) { | ||
arg++; | ||
} | ||
if (i === arg) { | ||
return args[i]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
getValue(value) { | ||
if (value) { | ||
if (!value.startsWith('"') && | ||
!value.startsWith('[') && | ||
!value.startsWith('{')) { | ||
value = `"${value}"`; | ||
} | ||
value = JSON.parse(value); | ||
} | ||
return value; | ||
} | ||
|
||
getAction(value) { | ||
let actions = ['add', 'remove', 'set', 'clear', 'get']; | ||
for (let action of actions) { | ||
if (this.options.hasFlag(action)) { | ||
return action; | ||
} | ||
} | ||
if (!value) { | ||
return 'get'; | ||
} | ||
if (Array.isArray(value) || typeof value === 'object') { | ||
return 'add'; | ||
} | ||
return 'set'; | ||
} | ||
} | ||
|
||
module.exports = ConfigurationUtilities; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.