serverless/lib/utils/download-template-from-examples.js
2024-05-29 11:51:04 -04:00

134 lines
4.0 KiB
JavaScript

import path from 'path'
import os from 'os'
import download from '@serverless/utils/download'
import fse from 'fs-extra'
import { promises as fsp } from 'fs'
import untildify from 'untildify'
import { renameService } from './rename-service.js'
import ServerlessError from '../serverless-error.js'
import dirExists from './fs/dir-exists.js'
import safeMoveFile from './fs/safe-move-file.js'
const resolveServiceName = (serviceDir) => {
let serviceName = path
.basename(serviceDir)
.toLowerCase()
.replace(/[^0-9a-z.]+/g, '-')
if (!serviceName.match(/^[a-z]/)) serviceName = `service-${serviceName}`
return serviceName
}
async function downloadTemplateFromExamples({
template,
name,
path: projectPath,
isLegacy,
}) {
const downloadUrl = 'https://github.com/serverless/examples/archive/v3.zip'
let pathToDirectory
if (isLegacy) {
pathToDirectory = `legacy/${template}`
} else {
pathToDirectory = template
}
const downloadServicePath = path.join(os.tmpdir(), 'examples')
const serviceDir = projectPath
? path.resolve(untildify(projectPath))
: process.cwd()
const serviceName = name || resolveServiceName(serviceDir)
// We do not want to run this check if project should be setup in current directory
if (serviceDir !== process.cwd() && (await dirExists(serviceDir))) {
const errorMessage = [
`The directory "${serviceDir}" already exists, and serverless will not overwrite it. `,
'Rename or move the directory and try again if you want serverless to create it"',
].join('')
throw new ServerlessError(errorMessage, 'TARGET_FOLDER_ALREADY_EXISTS')
}
const downloadOptions = {
timeout: 30000,
extract: true,
strip: 1,
mode: '755',
}
try {
await download(downloadUrl, downloadServicePath, downloadOptions)
} catch (err) {
throw new ServerlessError(
`Could not download template. Ensure that you are using the latest version of Serverless Framework: ${err.message}`,
'TEMPLATE_DOWNLOAD_FAILED',
)
}
// Examples repo has all examples nested in directories
const directory = path.join(downloadServicePath, pathToDirectory)
try {
if (serviceDir === process.cwd()) {
// ensure no template file already exists in current directory that we may overwrite
const topLevelContentList = await fsp.readdir(directory)
await Promise.all(
topLevelContentList.map(async (f) => {
let exists
try {
await fsp.access(path.join(process.cwd(), f))
exists = true
} catch (err) {
// Ignore, file does not exist
}
if (exists) {
const errorMessage = [
`The file or directory "${f}" already exists, and serverless will not overwrite it. `,
`Move it and try again if you want serverless to write a new "${f}"`,
].join('')
throw new ServerlessError(
errorMessage,
'TEMPLATE_FILE_ALREADY_EXISTS',
)
}
}),
)
await Promise.all(
topLevelContentList.map(async (f) => {
await safeMoveFile(path.join(directory, f), path.join(serviceDir, f))
}),
)
} else {
await safeMoveFile(directory, serviceDir)
}
} catch (err) {
if (err.code === 'ENOENT') {
throw new ServerlessError(
'Could not find provided template. Ensure that the template provided with "--template" exists.',
'INVALID_TEMPLATE',
)
}
if (err.code === 'EACCESS') {
const errorMessage = [
'Error unable to create a service in this directory. ',
'Please check that you have the required permissions to write to the directory',
].join('')
throw new ServerlessError(errorMessage, 'UNABLE_TO_CREATE_SERVICE')
}
throw err
}
// Cleanup whole downloaded dir
await fse.remove(downloadServicePath)
if (template !== 'plugin') {
renameService(serviceName, serviceDir)
}
return serviceName
}
export default downloadTemplateFromExamples