serverless/lib/utils/yamlAstParser.js

125 lines
4.0 KiB
JavaScript

'use strict';
const yaml = require('yaml-ast-parser');
const BbPromise = require('bluebird');
const fs = BbPromise.promisifyAll(require('fs'));
const _ = require('lodash');
const os = require('os');
const chalk = require('chalk');
const log = require('./log/serverlessLog');
const findKeyChain = (astContent) => {
let content = astContent;
const chain = [
content.key.value,
];
while (content.parent) {
content = content.parent;
if (content.key) {
chain.push(content.key.value);
}
}
return chain.reverse().join('.');
};
const parseAST = (ymlAstContent, astObject) => {
let newAstObject = astObject || {};
if (ymlAstContent.mappings && _.isArray(ymlAstContent.mappings)) {
_.forEach(ymlAstContent.mappings, (v) => {
if (!v.value) {
const errorMessage = [
'Serverless: ',
`${chalk.red('Your serverless.yml has an invalid value with key:')} `,
`${chalk.red(`"${v.key.value}"`)}`,
].join('');
log(errorMessage);
return;
}
if (v.key.kind === 0 && v.value.kind === 0) {
newAstObject[findKeyChain(v)] = v.value;
} else if (v.key.kind === 0 && v.value.kind === 2) {
newAstObject[findKeyChain(v)] = v.value;
newAstObject = parseAST(v.value, newAstObject);
} else if (v.key.kind === 0 && v.value.kind === 3) {
newAstObject[findKeyChain(v)] = v.value;
newAstObject = parseAST(v.value, newAstObject);
}
});
} else if (ymlAstContent.items && _.isArray(ymlAstContent.items)) {
_.forEach(ymlAstContent.items, (v, i) => {
if (v.kind === 0) {
const key = `${findKeyChain(ymlAstContent.parent)}[${i}]`;
newAstObject[key] = v;
}
});
}
return newAstObject;
};
const addNewArrayItem = (ymlFile, pathInYml, newValue) =>
fs.readFileAsync(ymlFile, 'utf8').then(yamlContent => {
const astObject = parseAST(yaml.load(yamlContent));
if (astObject[pathInYml] && astObject[pathInYml].items) {
let newArray = [];
_.forEach(astObject[pathInYml].items, (v) => {
newArray.push(v.value);
});
newArray = _.union(newArray, [newValue]);
const pathInYmlArrray = pathInYml.split('.');
let newObject = JSON.stringify(newArray);
_.forEach(pathInYmlArrray.reverse(), (v) => {
newObject = `{"${v}":${newObject}}`;
});
const beginning = yamlContent.substring(0, astObject[_.head(_.split(pathInYml, '.'))]
.parent.key.startPosition);
const end = yamlContent.substring(astObject[pathInYml].endPosition, yamlContent.length);
return fs.writeFileAsync(ymlFile, `${beginning}${yaml.dump(JSON.parse(newObject))}${end}`);
}
const pathInYmlArrray = pathInYml.split('.');
let newObject = JSON.stringify([newValue]);
_.forEach(pathInYmlArrray.reverse(), (v) => {
newObject = `{"${v}":${newObject}}`;
});
const beginning = yamlContent.substring(0, yamlContent.length);
return fs.writeFileAsync(ymlFile, `${beginning}${os.EOL}${yaml.dump(JSON.parse(newObject))}`);
});
const removeExistingArrayItem = (ymlFile, pathInYml, removeValue) =>
fs.readFileAsync(ymlFile, 'utf8').then(yamlContent => {
const astObject = parseAST(yaml.load(yamlContent));
if (astObject[pathInYml] && astObject[pathInYml].items) {
const newArray = [];
let newText = '';
_.forEach(astObject[pathInYml].items, (v) => {
newArray.push(v.value);
});
_.pull(newArray, removeValue);
if (!_.isEmpty(newArray)) {
const pathInYmlArrray = pathInYml.split('.');
let newObject = JSON.stringify(newArray);
_.forEach(pathInYmlArrray.reverse(), (v) => {
newObject = `{"${v}":${newObject}}`;
});
newText = yaml.dump(JSON.parse(newObject));
}
const beginning = yamlContent.substring(0, astObject[_.head(_.split(pathInYml, '.'))]
.parent.key.startPosition);
const end = yamlContent.substring(astObject[pathInYml].endPosition, yamlContent.length);
return fs.writeFileAsync(ymlFile, `${beginning}${newText}${end}`);
}
return BbPromise.resolve();
});
module.exports = {
addNewArrayItem,
removeExistingArrayItem,
};