const { basename , resolve : resolvePath } = require ( 'path' )
const chalk = require ( 'chalk' )
const { readFile } = require ( 'fs-promise' )
const { parse : parseDockerfile } = require ( 'docker-file-parser' )
const listPackage = {
scripts : {
start : 'serve ./content'
} ,
dependencies : {
serve : '^2.4.1'
}
}
module . exports = readMetaData
async function readMetaData ( path , {
deploymentType = 'npm' ,
deploymentName ,
quiet = false ,
strict = true ,
isStatic = false
} ) {
let pkg = { }
let nowConfig = null
let hasNowJson = false
let name
let description
try {
nowConfig = JSON . parse ( await readFile ( resolvePath ( path , 'now.json' ) ) )
hasNowJson = true
} catch ( err ) {
// if the file doesn't exist then that's fine; any other error bubbles up
if ( err . code !== 'ENOENT' ) {
const e = Error ( ` Failed to read JSON in " ${ path } /now.json" ` )
e . userError = true
throw e
}
}
// user can specify the type of deployment explicitly in the `now.json` file
// when both a package.json and Dockerfile exist
if ( hasNowJson && nowConfig . type ) {
deploymentType = nowConfig . type
}
if ( deploymentType === 'npm' ) {
if ( isStatic ) {
pkg = listPackage
} else {
try {
pkg = JSON . parse ( await readFile ( resolvePath ( path , 'package.json' ) ) )
} catch ( err ) {
const e = Error ( ` Failed to read JSON in " ${ path } /package.json" ` )
e . userError = true
throw e
}
}
if ( strict && ( ! pkg . scripts || ( ! pkg . scripts . start && ! pkg . scripts [ 'now-start' ] ) ) ) {
const e = Error ( 'Missing `start` (or `now-start`) script in `package.json`. ' +
'See: https://docs.npmjs.com/cli/start.' )
e . userError = true
throw e
}
if ( ! deploymentName ) {
if ( typeof pkg . name === 'string' ) {
name = pkg . name
} else {
name = basename ( path )
if ( ! quiet && ! isStatic ) {
console . log ( ` > No \` name \` in \` package.json \` , using ${ chalk . bold ( name ) } ` )
}
}
}
description = pkg . description
} else if ( deploymentType === 'docker' ) {
let docker
try {
const dockerfile = await readFile ( resolvePath ( path , 'Dockerfile' ) , 'utf8' )
docker = parseDockerfile ( dockerfile , { includeComments : true } )
} catch ( err ) {
const e = Error ( ` Failed to parse " ${ path } /Dockerfile" ` )
e . userError = true
throw e
}
if ( strict && docker . length <= 0 ) {
const e = Error ( 'No commands found in `Dockerfile`' )
e . userError = true
throw e
}
const labels = { }
docker
. filter ( cmd => cmd . name === 'LABEL' )
. forEach ( ( { args } ) => {
for ( const key in args ) {
if ( ! { } . hasOwnProperty . call ( args , key ) ) {
continue
}
// unescape and convert into string
try {
labels [ key ] = JSON . parse ( args [ key ] )
} catch ( err ) {
const e = Error ( ` Error parsing value for LABEL ${ key } in \` Dockerfile \` ` )
e . userError = true
throw e
}
}
} )
if ( ! deploymentName ) {
if ( labels . name ) {
name = labels . name
} else {
name = basename ( path )
if ( ! quiet ) {
console . log ( ` > No \` name \` LABEL in \` Dockerfile \` , using ${ chalk . bold ( name ) } ` )
}
}
}
description = labels . description
} else {
throw new TypeError ( ` Unsupported "deploymentType": ${ deploymentType } ` )
}
if ( deploymentName ) {
name = deploymentName
}
if ( pkg . now ) {
// if the project has both a `now.json` and `now` Object in the `package.json`
// file, then fail hard and let the user know that they need to pick one or the
// other
if ( hasNowJson ) {
const e = new Error ( 'You have a `now` configuration field inside `package.json`, but configuration is also present in `now.json`! Please ensure there\'s a single source of configuration by removing one' )
e . userError = true
throw e
} else {
nowConfig = pkg . now
}
}
return {
name ,
description ,
deploymentType ,
pkg ,
nowConfig ,
hasNowJson
}
}