Skip to content

Commit e33fedd

Browse files
committed
Initial Commit
0 parents  commit e33fedd

10 files changed

Lines changed: 2576 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
text/fixtures/tmp

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Create a `package.json` like a champion
2+
3+
This is a fully featured `package.json` scaffolding tool. It goes above and beyond the basic `npm init`
4+
by supporting (almost) all of the keys you can set in a `package.json`. It can be used as a simple cli
5+
tool, or inside your other package scaffolding tools.
6+
7+
## Usage
8+
9+
```
10+
$ npm init package-json
11+
```
12+
13+
With `npm@6` this will run this package with `npx`. If you are on an earlier version of `npm` you will
14+
need to install globally and run directly:
15+
16+
```
17+
$ npm install -g create-package-json
18+
$ create-package-json
19+
```
20+
21+
*NOTE:* This is a work in progress, more to come on docs and there are some missing options at the moment.

bin/create-package-json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env node
2+
'use strict'
3+
// vim: set ft=javascript:ts=2:sw=2
4+
const program = require('commander')
5+
const pkg = require('../package.json')
6+
const createPackageJson = require('../')
7+
const arrayFromList = require('../lib/array-from-list')
8+
9+
let directory
10+
11+
program
12+
.version(pkg.version)
13+
.arguments('<directory>')
14+
.action((dir) => directory = dir)
15+
.option('--ignore-existing', 'Ignore existing package.json')
16+
.option('--no-prompt', 'Skip prompts and just use input options')
17+
.option('--spacer [json spacer]', 'Format character for package json', 2)
18+
.option('--name [name]', 'The package name')
19+
.option('--scope [scope]', 'The package scope')
20+
.option('--ver [version]', 'The package version')
21+
.option('--description [description]', 'The package description')
22+
.option('--author [author]', 'The package author')
23+
.option('--repository [repository]', 'The package repository')
24+
.option('--keywords [keywords]', 'The package keywords')
25+
.option('--license [license]', 'The package license')
26+
.option('--main [main]', 'The package main entry point')
27+
.option('--private', 'This is a private package')
28+
.option('--dependencies [dependencies]', 'Package dependencies')
29+
.option('--dev-dependencies [dependencies]', 'Package dev dependencies')
30+
.option('--scripts [scripts]', 'Package scripts')
31+
.parse(process.argv)
32+
33+
// Run the process
34+
createPackageJson({
35+
directory: directory,
36+
ignoreExisting: program.ignoreExisting,
37+
noPrompt: program.noPrompt,
38+
spacer: program.spacer,
39+
// check if string because there is a .name getter function, same with description
40+
name: typeof program.name === 'string' ? program.name : undefined,
41+
scope: program.scope,
42+
version: program.ver,
43+
description: typeof program.description === 'string' ? program.description : undefined,
44+
author: program.author,
45+
repository: program.repository,
46+
keywords: program.keywords,
47+
license: program.license,
48+
main: program.main,
49+
private: program.private,
50+
dependencies: arrayFromList(program.dependencies),
51+
devDependencies: arrayFromList(program.devDependencies)
52+
})

index.js

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
'use strict'
2+
// vim: set ft=javascript ts=2 sw=2:
3+
const path = require('path')
4+
const fs = require('fs-extra')
5+
const shell = require('shelljs')
6+
const defined = require('defined')
7+
const prompt = require('./lib/prompts')
8+
const arrayOrUndefined = require('./lib/array-or-undefined')
9+
10+
// @TODO https://docs.npmjs.com/files/package.json
11+
// man
12+
// bin
13+
// files
14+
// browser
15+
// directories
16+
// config
17+
// peerDependencies
18+
// bundledDependencies
19+
// optionalDependencies
20+
// engines
21+
// engineStrict
22+
// cpu
23+
// publishConfig
24+
25+
module.exports = async function createPackageJson (options = {}) {
26+
// Construct all the options from the input
27+
const opts = await buildOptions(options)
28+
29+
// Merge it back together and write it out
30+
return write((opts.noPrompt === true) ? opts : await prompt(opts, options), format(opts, pkg))
31+
}
32+
33+
module.exports.buildOptions = buildOptions
34+
async function buildOptions (input = {}) {
35+
// Removed undefined values from input
36+
const options = Object.keys(input).reduce((o, key) => {
37+
if (typeof input[key] !== 'undefined') {
38+
o[key] = input[key]
39+
}
40+
return o
41+
}, {})
42+
43+
// Option defaults
44+
const opts = Object.assign({
45+
spacer: 2,
46+
directory: process.cwd(),
47+
ignoreExisting: false,
48+
noPrompt: false,
49+
silent: false
50+
}, options)
51+
52+
// Read existing package.json
53+
opts.pkgPath = path.join(opts.directory, 'package.json')
54+
let pkg = {}
55+
if (opts.ignoreExisting === true) {
56+
try {
57+
pkg = await fs.readJSON(opts.pkgPath)
58+
} catch (e) {
59+
// ignore if missing or unreadable
60+
}
61+
}
62+
63+
// Set things from opts, package.json or defaults
64+
opts.version = opts.version || pkg.version || '1.0.0'
65+
opts.description = opts.description || pkg.description || ''
66+
opts.author = opts.author || pkg.author || await getAuthor()
67+
opts.repository = opts.repository || (pkg.repository && pkg.repository.url) || await readGitRemote(opts.directory)
68+
opts.keywords = arrayOrUndefined(opts.keywords) || pkg.keywords || []
69+
opts.license = opts.license || pkg.license || 'ISC'
70+
opts.main = opts.main || pkg.main || 'index.js'
71+
opts.private = defined(opts.private, pkg.private)
72+
opts.dependencies = mapDepToVersionString(arrayOrUndefined(opts.dependencies) || pkg.dependencies || [])
73+
opts.devDependencies = mapDepToVersionString(arrayOrUndefined(opts.devDependencies) || pkg.devDependencies || [])
74+
75+
// Merge together scripts from opts and package.json
76+
opts.scripts = Object.assign({}, opts.scripts || {}, pkg.scripts || {})
77+
78+
// Get name and scope, if not from options from cwd
79+
const {name, scope} = getScopeAndName(opts.scope, opts.name || pkg.name, opts.directory)
80+
opts.name = name
81+
opts.scope = scope
82+
83+
return opts
84+
}
85+
86+
module.exports.format = format
87+
function format (opts, pkg = {}) {
88+
pkg.name = opts.scope ? `${opts.scope}/${opts.name}` : opts.name
89+
pkg.version = opts.version
90+
pkg.description = opts.description
91+
pkg.author = opts.author
92+
pkg.keywords = opts.keywords
93+
pkg.license = opts.license
94+
pkg.main = opts.main
95+
if (opts.repository) {
96+
pkg.repository = {
97+
type: 'git',
98+
url: opts.repository
99+
}
100+
}
101+
pkg.scripts = opts.scripts
102+
if (opts.private === true) {
103+
pkg.private = true
104+
}
105+
return pkg
106+
}
107+
108+
module.exports.write = write
109+
async function write (opts, pkg) {
110+
// Write package json
111+
await fs.outputJSON(opts.pkgPath, pkg, {
112+
spaces: opts.spacer || 2
113+
})
114+
115+
// Run installs
116+
if (opts.dependencies && opts.dependencies.length) {
117+
await shell.exec(`npm i --save ${opts.dependencies.join(' ')}`, {
118+
cwd: opts.directory,
119+
silent: opts.silent
120+
})
121+
}
122+
if (opts.devDependencies && opts.devDependencies.length) {
123+
await shell.exec(`npm i --save-dev ${opts.devDependencies.join(' ')}`, {
124+
cwd: opts.directory,
125+
silent: opts.silent
126+
})
127+
}
128+
129+
// Read full package back to return
130+
return fs.readJSON(opts.pkgPath)
131+
}
132+
133+
//
134+
// Helper Functions
135+
//
136+
137+
function readGitRemote (dir) {
138+
// Taken from npm: https://github.com/npm/init-package-json/blob/latest/default-input.js#L188-L208
139+
return new Promise((resolve) => {
140+
fs.readFile(path.join(dir, '.git', 'config'), 'utf8', function (err, conf) {
141+
if (err || !conf) {
142+
return resolve()
143+
}
144+
conf = conf.split(/\r?\n/)
145+
const i = conf.indexOf('[remote "origin"]')
146+
let u
147+
if (i !== -1) {
148+
// Check if one of the next two lines is the remote url
149+
u = conf[i + 1]
150+
if (!u.match(/^\s*url =/)) {
151+
u = conf[i + 2]
152+
}
153+
if (!u.match(/^\s*url =/)) {
154+
u = null
155+
} else {
156+
u = u.replace(/^\s*url = /, '')
157+
}
158+
}
159+
160+
// Replace github url
161+
if (u && u.match(/^git@github.com:/)) {
162+
u = u.replace(/^git@github.com:/, 'https://github.com/')
163+
}
164+
165+
resolve(u)
166+
})
167+
})
168+
}
169+
170+
function getAuthor () {
171+
const name = shell.exec('git config --get user.name', { silent: true }).stdout.trim()
172+
const email = shell.exec('git config --get user.email', { silent: true }).stdout.trim()
173+
if (!name) {
174+
return
175+
}
176+
return `${name} <${email}>`
177+
}
178+
179+
function getScopeAndName (scope, name, cwd) {
180+
// If no package name, get cwd base name
181+
if (!name) {
182+
name = path.basename(cwd)
183+
}
184+
185+
// If no scope, see if name has a scope in it
186+
if (!scope && name && name.startsWith('@')) {
187+
[scope, name] = name.split('/')
188+
}
189+
190+
// If still no scope, see if one directory up starts with an @
191+
const dirname = path.basename(path.dirname(cwd))
192+
if (dirname.startsWith('@')) {
193+
scope = dirname
194+
}
195+
196+
return { name, scope }
197+
}
198+
199+
function mapDepToVersionString (deps) {
200+
return Array.isArray(deps) ? deps : Object.keys(deps).map((name) => `${name}@${deps[name]}`)
201+
}

lib/array-from-list.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict'
2+
// vim: set ft=javascript:ts=2:sw=2
3+
4+
module.exports = function arrayFromList (list) {
5+
let arr = list || []
6+
if (typeof list === 'string') {
7+
arr = list.split(/\s*,\s*/g)
8+
}
9+
return arr.reduce((a, item) => {
10+
if (item !== '') {
11+
a.push(item)
12+
}
13+
return a
14+
}, [])
15+
}

lib/array-or-undefined.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
'use strict'
3+
// vim: set ft=javascript ts=2 sw=2:
4+
5+
module.exports = function arrayOrUndefined (prop) {
6+
return prop && prop.length ? prop : undefined
7+
}

0 commit comments

Comments
 (0)