Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/init.js')
-rw-r--r--lib/init.js190
1 files changed, 160 insertions, 30 deletions
diff --git a/lib/init.js b/lib/init.js
index 81c673388..7d7f6bab3 100644
--- a/lib/init.js
+++ b/lib/init.js
@@ -1,6 +1,14 @@
+const fs = require('fs')
+const { relative, resolve } = require('path')
+const mkdirp = require('mkdirp-infer-owner')
const initJson = require('init-package-json')
const npa = require('npm-package-arg')
+const rpj = require('read-package-json-fast')
+const libexec = require('libnpmexec')
+const parseJSON = require('json-parse-even-better-errors')
+const mapWorkspaces = require('@npmcli/map-workspaces')
+const getLocationMsg = require('./exec/get-workspace-location-msg.js')
const BaseCommand = require('./base-command.js')
class Init extends BaseCommand {
@@ -10,6 +18,11 @@ class Init extends BaseCommand {
}
/* istanbul ignore next - see test/lib/load-all-commands.js */
+ static get params () {
+ return ['workspace', 'workspaces']
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
static get name () {
return 'init'
}
@@ -27,42 +40,107 @@ class Init extends BaseCommand {
this.init(args).then(() => cb()).catch(cb)
}
+ execWorkspaces (args, filters, cb) {
+ this.initWorkspaces(args, filters).then(() => cb()).catch(cb)
+ }
+
async init (args) {
- // the new npx style way
+ // npm exec style
+ if (args.length)
+ return (await this.execCreate({ args, path: process.cwd() }))
+
+ // no args, uses classic init-package-json boilerplate
+ await this.template()
+ }
+
+ async initWorkspaces (args, filters) {
+ // reads package.json for the top-level folder first, by doing this we
+ // ensure the command throw if no package.json is found before trying
+ // to create a workspace package.json file or its folders
+ const pkg = await rpj(resolve(this.npm.localPrefix, 'package.json'))
+ const wPath = filterArg => resolve(this.npm.localPrefix, filterArg)
+
+ // npm-exec style, runs in the context of each workspace filter
if (args.length) {
- const initerName = args[0]
- let packageName = initerName
- if (/^@[^/]+$/.test(initerName))
- packageName = initerName + '/create'
- else {
- const req = npa(initerName)
- if (req.type === 'git' && req.hosted) {
- const { user, project } = req.hosted
- packageName = initerName
- .replace(user + '/' + project, user + '/create-' + project)
- } else if (req.registry) {
- packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
- if (req.rawSpec)
- packageName += '@' + req.rawSpec
- } else {
- throw Object.assign(new Error(
- 'Unrecognized initializer: ' + initerName +
- '\nFor more package binary executing power check out `npx`:' +
- '\nhttps://www.npmjs.com/package/npx'
- ), { code: 'EUNSUPPORTED' })
- }
+ for (const filterArg of filters) {
+ const path = wPath(filterArg)
+ await mkdirp(path)
+ await this.execCreate({ args, path })
+ await this.setWorkspace({ pkg, workspacePath: path })
+ }
+ return
+ }
+
+ // no args, uses classic init-package-json boilerplate
+ for (const filterArg of filters) {
+ const path = wPath(filterArg)
+ await mkdirp(path)
+ await this.template(path)
+ await this.setWorkspace({ pkg, workspacePath: path })
+ }
+ }
+
+ async execCreate ({ args, path }) {
+ const [initerName, ...otherArgs] = args
+ let packageName = initerName
+
+ if (/^@[^/]+$/.test(initerName))
+ packageName = initerName + '/create'
+ else {
+ const req = npa(initerName)
+ if (req.type === 'git' && req.hosted) {
+ const { user, project } = req.hosted
+ packageName = initerName
+ .replace(user + '/' + project, user + '/create-' + project)
+ } else if (req.registry) {
+ packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
+ if (req.rawSpec)
+ packageName += '@' + req.rawSpec
+ } else {
+ throw Object.assign(new Error(
+ 'Unrecognized initializer: ' + initerName +
+ '\nFor more package binary executing power check out `npx`:' +
+ '\nhttps://www.npmjs.com/package/npx'
+ ), { code: 'EUNSUPPORTED' })
}
- this.npm.config.set('package', [])
- const newArgs = [packageName, ...args.slice(1)]
- return new Promise((res, rej) => {
- this.npm.commands.exec(newArgs, er => er ? rej(er) : res())
- })
}
- // the old way
- const dir = process.cwd()
+ const newArgs = [packageName, ...otherArgs]
+ const cache = this.npm.config.get('cache')
+ const { color } = this.npm.flatOptions
+ const {
+ flatOptions,
+ localBin,
+ log,
+ globalBin,
+ output,
+ } = this.npm
+ const locationMsg = await getLocationMsg({ color, path })
+ const runPath = path
+ const scriptShell = this.npm.config.get('script-shell') || undefined
+ const yes = this.npm.config.get('yes')
+
+ await libexec({
+ ...flatOptions,
+ args: newArgs,
+ cache,
+ color,
+ localBin,
+ locationMsg,
+ log,
+ globalBin,
+ output,
+ path,
+ runPath,
+ scriptShell,
+ yes,
+ })
+ }
+
+ async template (path = process.cwd()) {
this.npm.log.pause()
this.npm.log.disableProgress()
+
const initFile = this.npm.config.get('init-module')
if (!this.npm.config.get('yes') && !this.npm.config.get('force')) {
this.npm.output([
@@ -78,9 +156,10 @@ class Init extends BaseCommand {
'Press ^C at any time to quit.',
].join('\n'))
}
+
// XXX promisify init-package-json
await new Promise((res, rej) => {
- initJson(dir, initFile, this.npm.config, (er, data) => {
+ initJson(path, initFile, this.npm.config, (er, data) => {
this.npm.log.resume()
this.npm.log.enableProgress()
this.npm.log.silly('package data', data)
@@ -97,5 +176,56 @@ class Init extends BaseCommand {
})
})
}
+
+ async setWorkspace ({ pkg, workspacePath }) {
+ const workspaces = await mapWorkspaces({ cwd: this.npm.localPrefix, pkg })
+
+ // skip setting workspace if current package.json glob already satisfies it
+ for (const wPath of workspaces.values()) {
+ if (wPath === workspacePath)
+ return
+ }
+
+ // if a create-pkg didn't generate a package.json at the workspace
+ // folder level, it might not be recognized as a workspace by
+ // mapWorkspaces, so we're just going to avoid touching the
+ // top-level package.json
+ try {
+ fs.statSync(resolve(workspacePath, 'package.json'))
+ } catch (err) {
+ return
+ }
+
+ let manifest
+ try {
+ manifest =
+ fs.readFileSync(resolve(this.npm.localPrefix, 'package.json'), 'utf-8')
+ } catch (error) {
+ throw new Error('package.json not found')
+ }
+
+ try {
+ manifest = parseJSON(manifest)
+ } catch (error) {
+ throw new Error(`Invalid package.json: ${error}`)
+ }
+
+ if (!manifest.workspaces)
+ manifest.workspaces = []
+
+ manifest.workspaces.push(relative(this.npm.localPrefix, workspacePath))
+
+ // format content
+ const {
+ [Symbol.for('indent')]: indent,
+ [Symbol.for('newline')]: newline,
+ } = manifest
+
+ const content = (JSON.stringify(manifest, null, indent) + '\n')
+ .replace(/\n/g, newline)
+
+ fs.writeFileSync(resolve(this.npm.localPrefix, 'package.json'), content)
+ }
}
+
module.exports = Init