mirror of
https://github.com/serverless/serverless.git
synced 2025-12-08 19:46:03 +00:00
* fix: remove bluebird from zip-service * fix: remove bluebird and set concurrency limits for packaging
181 lines
5.7 KiB
JavaScript
181 lines
5.7 KiB
JavaScript
import yaml from 'yaml-ast-parser'
|
|
import fs from 'fs'
|
|
import _ from 'lodash'
|
|
import os from 'os'
|
|
import utils from '@serverlessinc/sf-core/src/utils.js'
|
|
|
|
const { log } = utils
|
|
|
|
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 && Array.isArray(ymlAstContent.mappings)) {
|
|
ymlAstContent.mappings.forEach((v) => {
|
|
if (!v.value) {
|
|
log.error(
|
|
`Your serverless.yml has an invalid value with key: "${v.key.value}"`,
|
|
)
|
|
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 || v.value.kind === 3)
|
|
) {
|
|
newAstObject[findKeyChain(v)] = v.value
|
|
newAstObject = parseAST(v.value, newAstObject)
|
|
}
|
|
})
|
|
} else if (ymlAstContent.items && Array.isArray(ymlAstContent.items)) {
|
|
ymlAstContent.items.forEach((v, i) => {
|
|
if (v.kind === 0) {
|
|
const key = `${findKeyChain(ymlAstContent.parent)}[${i}]`
|
|
newAstObject[key] = v
|
|
}
|
|
})
|
|
}
|
|
|
|
return newAstObject
|
|
}
|
|
|
|
const constructPlainObject = (ymlAstContent, branchObject) => {
|
|
const newbranchObject = branchObject || {}
|
|
if (ymlAstContent.mappings && Array.isArray(ymlAstContent.mappings)) {
|
|
ymlAstContent.mappings.forEach((v) => {
|
|
if (!v.value) {
|
|
// no need to log twice, parseAST will log errors
|
|
return
|
|
}
|
|
|
|
if (v.key.kind === 0 && v.value.kind === 0) {
|
|
newbranchObject[v.key.value] = v.value.value
|
|
} else if (v.key.kind === 0 && v.value.kind === 2) {
|
|
newbranchObject[v.key.value] = constructPlainObject(v.value, {})
|
|
} else if (v.key.kind === 0 && v.value.kind === 3) {
|
|
const plainArray = []
|
|
v.value.items.forEach((c) => {
|
|
plainArray.push(c.value)
|
|
})
|
|
newbranchObject[v.key.value] = plainArray
|
|
}
|
|
})
|
|
}
|
|
|
|
return newbranchObject
|
|
}
|
|
|
|
const addNewArrayItem = (ymlFile, pathInYml, newValue) =>
|
|
fs.readFileAsync(ymlFile, 'utf8').then((yamlContent) => {
|
|
const rawAstObject = yaml.load(yamlContent)
|
|
const astObject = parseAST(rawAstObject)
|
|
const plainObject = constructPlainObject(rawAstObject)
|
|
const pathInYmlArray = pathInYml.split('.')
|
|
|
|
let currentNode = plainObject
|
|
for (let i = 0; i < pathInYmlArray.length - 1; i++) {
|
|
const propertyName = pathInYmlArray[i]
|
|
const property = currentNode[propertyName]
|
|
if (!property || _.isObject(property)) {
|
|
currentNode[propertyName] = property || {}
|
|
currentNode = currentNode[propertyName]
|
|
} else {
|
|
throw new Error(`${property} can only be undefined or an object!`)
|
|
}
|
|
}
|
|
|
|
const arrayPropertyName = _.last(pathInYmlArray)
|
|
let arrayProperty = currentNode[arrayPropertyName]
|
|
if (!arrayProperty || Array.isArray(arrayProperty)) {
|
|
arrayProperty = arrayProperty || []
|
|
} else {
|
|
throw new Error(`${arrayProperty} can only be undefined or an array!`)
|
|
}
|
|
currentNode[arrayPropertyName] = _.union(arrayProperty, [newValue])
|
|
|
|
const branchToReplaceName = pathInYmlArray[0]
|
|
const newObject = {}
|
|
newObject[branchToReplaceName] = plainObject[branchToReplaceName]
|
|
const newText = yaml.dump(newObject)
|
|
if (astObject[branchToReplaceName]) {
|
|
const beginning = yamlContent.substring(
|
|
0,
|
|
astObject[branchToReplaceName].parent.key.startPosition,
|
|
)
|
|
const end = yamlContent.substring(
|
|
astObject[branchToReplaceName].endPosition,
|
|
yamlContent.length,
|
|
)
|
|
return fs.writeFileAsync(ymlFile, `${beginning}${newText}${end}`)
|
|
}
|
|
return fs.writeFileAsync(ymlFile, `${yamlContent}${os.EOL}${newText}`)
|
|
})
|
|
|
|
const removeExistingArrayItem = async (ymlFile, pathInYml, removeValue) =>
|
|
fs.readFileAsync(ymlFile, 'utf8').then((yamlContent) => {
|
|
const rawAstObject = yaml.load(yamlContent)
|
|
const astObject = parseAST(rawAstObject)
|
|
|
|
if (astObject[pathInYml] && astObject[pathInYml].items) {
|
|
const plainObject = constructPlainObject(rawAstObject)
|
|
const pathInYmlArray = pathInYml.split('.')
|
|
|
|
let currentNode = plainObject
|
|
const pathInObjectTree = []
|
|
for (let i = 0; i < pathInYmlArray.length - 1; i++) {
|
|
pathInObjectTree.push(currentNode)
|
|
currentNode = currentNode[pathInYmlArray[i]]
|
|
}
|
|
const arrayPropertyName = _.last(pathInYmlArray)
|
|
const arrayProperty = currentNode[arrayPropertyName]
|
|
_.pull(arrayProperty, removeValue)
|
|
|
|
if (!arrayProperty.length) {
|
|
delete currentNode[arrayPropertyName]
|
|
pathInObjectTree.push(currentNode)
|
|
for (let i = pathInObjectTree.length - 1; i > 0; i--) {
|
|
if (Object.keys(pathInObjectTree[i]).length > 0) {
|
|
break
|
|
}
|
|
delete pathInObjectTree[i - 1][pathInYmlArray[i - 1]]
|
|
}
|
|
}
|
|
|
|
const headObjectPath = pathInYmlArray[0]
|
|
let newText = ''
|
|
|
|
if (plainObject[headObjectPath]) {
|
|
const newObject = {}
|
|
newObject[headObjectPath] = plainObject[headObjectPath]
|
|
newText = yaml.dump(newObject)
|
|
}
|
|
const beginning = yamlContent.substring(
|
|
0,
|
|
astObject[headObjectPath].parent.key.startPosition,
|
|
)
|
|
const end = yamlContent.substring(
|
|
astObject[pathInYml].endPosition,
|
|
yamlContent.length,
|
|
)
|
|
return fs.writeFileAsync(ymlFile, `${beginning}${newText}${end}`)
|
|
}
|
|
return Promise.resolve()
|
|
})
|
|
|
|
export default {
|
|
addNewArrayItem,
|
|
removeExistingArrayItem,
|
|
}
|