/* globals config, dirname, package, basename, yes, prompt */
|
|
const fs = require('fs/promises')
|
const path = require('path')
|
const validateLicense = require('validate-npm-package-license')
|
const validateName = require('validate-npm-package-name')
|
const npa = require('npm-package-arg')
|
const semver = require('semver')
|
|
// more popular packages should go here, maybe?
|
const isTestPkg = (p) => !!p.match(/^(expresso|mocha|tap|coffee-script|coco|streamline)$/)
|
|
const invalid = (msg) => Object.assign(new Error(msg), { notValid: true })
|
|
const readDeps = (test, excluded) => async () => {
|
const dirs = await fs.readdir('node_modules').catch(() => null)
|
|
if (!dirs) {
|
return
|
}
|
|
const deps = {}
|
for (const dir of dirs) {
|
if (dir.match(/^\./) || test !== isTestPkg(dir) || excluded[dir]) {
|
continue
|
}
|
|
const dp = path.join(dirname, 'node_modules', dir, 'package.json')
|
const p = await fs.readFile(dp, 'utf8').then((d) => JSON.parse(d)).catch(() => null)
|
|
if (!p || !p.version || p?._requiredBy?.some((r) => r === '#USER')) {
|
continue
|
}
|
|
deps[dir] = config.get('save-exact') ? p.version : config.get('save-prefix') + p.version
|
}
|
|
return deps
|
}
|
|
const getConfig = (key) => {
|
// dots take precedence over dashes
|
const def = config?.defaults?.[`init.${key}`]
|
const val = config.get(`init.${key}`)
|
return (val !== def && val) ? val : config.get(`init-${key.replace(/\./g, '-')}`)
|
}
|
|
const getName = () => {
|
const rawName = package.name || basename
|
let name = rawName
|
.replace(/^node-|[.-]js$/g, '')
|
.replace(/\s+/g, ' ')
|
.replace(/ /g, '-')
|
.toLowerCase()
|
|
let spec
|
try {
|
spec = npa(name)
|
} catch {
|
spec = {}
|
}
|
|
let scope = config.get('scope')
|
|
if (scope) {
|
if (scope.charAt(0) !== '@') {
|
scope = '@' + scope
|
}
|
if (spec.scope) {
|
name = scope + '/' + spec.name.split('/')[1]
|
} else {
|
name = scope + '/' + name
|
}
|
}
|
|
return name
|
}
|
|
const name = getName()
|
exports.name = yes ? name : prompt('package name', name, (data) => {
|
const its = validateName(data)
|
if (its.validForNewPackages) {
|
return data
|
}
|
const errors = (its.errors || []).concat(its.warnings || [])
|
return invalid(`Sorry, ${errors.join(' and ')}.`)
|
})
|
|
const version = package.version || getConfig('version') || '1.0.0'
|
exports.version = yes ? version : prompt('version', version, (v) => {
|
if (semver.valid(v)) {
|
return v
|
}
|
return invalid(`Invalid version: "${v}"`)
|
})
|
|
if (!package.description) {
|
exports.description = yes ? '' : prompt('description')
|
}
|
|
if (!package.main) {
|
exports.main = async () => {
|
const files = await fs.readdir(dirname)
|
.then(list => list.filter((f) => f.match(/\.js$/)))
|
.catch(() => [])
|
|
let index
|
if (files.includes('index.js')) {
|
index = 'index.js'
|
} else if (files.includes('main.js')) {
|
index = 'main.js'
|
} else if (files.includes(basename + '.js')) {
|
index = basename + '.js'
|
} else {
|
index = files[0] || 'index.js'
|
}
|
|
return yes ? index : prompt('entry point', index)
|
}
|
}
|
|
if (!package.bin) {
|
exports.bin = async () => {
|
try {
|
const d = await fs.readdir(path.resolve(dirname, 'bin'))
|
// just take the first js file we find there, or nada
|
let r = d.find(f => f.match(/\.js$/))
|
if (r) {
|
r = `bin/${r}`
|
}
|
return r
|
} catch {
|
// no bins
|
}
|
}
|
}
|
|
exports.directories = async () => {
|
const dirs = await fs.readdir(dirname)
|
|
const res = dirs.reduce((acc, d) => {
|
if (/^examples?$/.test(d)) {
|
acc.example = d
|
} else if (/^tests?$/.test(d)) {
|
acc.test = d
|
} else if (/^docs?$/.test(d)) {
|
acc.doc = d
|
} else if (d === 'man') {
|
acc.man = d
|
} else if (d === 'lib') {
|
acc.lib = d
|
}
|
|
return acc
|
}, {})
|
|
return Object.keys(res).length === 0 ? undefined : res
|
}
|
|
if (!package.dependencies) {
|
exports.dependencies = readDeps(false, package.devDependencies || {})
|
}
|
|
if (!package.devDependencies) {
|
exports.devDependencies = readDeps(true, package.dependencies || {})
|
}
|
|
// MUST have a test script!
|
if (!package.scripts) {
|
const scripts = package.scripts || {}
|
const notest = 'echo "Error: no test specified" && exit 1'
|
exports.scripts = async () => {
|
const d = await fs.readdir(path.join(dirname, 'node_modules')).catch(() => [])
|
|
// check to see what framework is in use, if any
|
let command
|
if (!scripts.test || scripts.test === notest) {
|
const commands = {
|
tap: 'tap test/*.js',
|
expresso: 'expresso test',
|
mocha: 'mocha',
|
}
|
for (const [k, v] of Object.entries(commands)) {
|
if (d.includes(k)) {
|
command = v
|
}
|
}
|
}
|
|
const promptArgs = ['test command', (t) => t || notest]
|
if (command) {
|
promptArgs.splice(1, 0, command)
|
}
|
scripts.test = yes ? command || notest : prompt(...promptArgs)
|
|
return scripts
|
}
|
}
|
|
if (!package.repository) {
|
exports.repository = async () => {
|
const gconf = await fs.readFile('.git/config', 'utf8').catch(() => '')
|
const lines = gconf.split(/\r?\n/)
|
|
let url
|
const i = lines.indexOf('[remote "origin"]')
|
|
if (i !== -1) {
|
url = gconf[i + 1]
|
if (!url.match(/^\s*url =/)) {
|
url = gconf[i + 2]
|
}
|
if (!url.match(/^\s*url =/)) {
|
url = null
|
} else {
|
url = url.replace(/^\s*url = /, '')
|
}
|
}
|
|
if (url && url.match(/^git@github.com:/)) {
|
url = url.replace(/^git@github.com:/, 'https://github.com/')
|
}
|
|
return yes ? url || '' : prompt('git repository', url || undefined)
|
}
|
}
|
|
if (!package.keywords) {
|
exports.keywords = yes ? '' : prompt('keywords', (data) => {
|
if (!data) {
|
return
|
}
|
if (Array.isArray(data)) {
|
data = data.join(' ')
|
}
|
if (typeof data !== 'string') {
|
return data
|
}
|
return data.split(/[\s,]+/)
|
})
|
}
|
|
if (!package.author) {
|
const authorName = getConfig('author.name')
|
exports.author = authorName
|
? {
|
name: authorName,
|
email: getConfig('author.email'),
|
url: getConfig('author.url'),
|
}
|
: yes ? '' : prompt('author')
|
}
|
|
const license = package.license || getConfig('license') || 'ISC'
|
exports.license = yes ? license : prompt('license', license, (data) => {
|
const its = validateLicense(data)
|
if (its.validForNewPackages) {
|
return data
|
}
|
const errors = (its.errors || []).concat(its.warnings || [])
|
return invalid(`Sorry, ${errors.join(' and ')}.`)
|
})
|