diff --git a/packages/pnpm/package.json b/packages/pnpm/package.json index d2f0f3793c..89b08f526c 100644 --- a/packages/pnpm/package.json +++ b/packages/pnpm/package.json @@ -84,7 +84,8 @@ "text-table": "0.2.0", "tree-kill": "1.2.1", "update-notifier": "2.5.0", - "version-selector-type": "2.0.1" + "version-selector-type": "2.0.1", + "write-json-file": "3.2.0" }, "devDependencies": { "@pnpm/assert-project": "link:../../privatePackages/assert-project", @@ -127,7 +128,6 @@ "ts-node": "8.1.0", "tslint": "5.16.0", "typescript": "3.4.5", - "write-json-file": "3.2.0", "write-pkg": "4.0.0", "write-yaml-file": "2.0.0" }, diff --git a/packages/pnpm/src/bin/pnpm.ts b/packages/pnpm/src/bin/pnpm.ts index 7117938f20..02b0811516 100755 --- a/packages/pnpm/src/bin/pnpm.ts +++ b/packages/pnpm/src/bin/pnpm.ts @@ -37,11 +37,9 @@ if (argv.includes('--help') || argv.includes('-h') || argv.includes('--h')) { case 'login': case 'logout': case 'owner': - case 'pack': case 'ping': case 'prefix': case 'profile': - case 'publish': case 'repo': case 's': case 'se': @@ -78,5 +76,6 @@ async function runPnpm () { async function passThruToNpm () { const runNpm = (await import('../cmd/runNpm')).default - runNpm(argv) + const { status } = runNpm(argv) + process.exit(status) } diff --git a/packages/pnpm/src/cmd/help.ts b/packages/pnpm/src/cmd/help.ts index 37d0a447da..b2a2559b5b 100644 --- a/packages/pnpm/src/cmd/help.ts +++ b/packages/pnpm/src/cmd/help.ts @@ -215,6 +215,20 @@ function getHelpText (command: string) { Removes extraneous packages ` + case 'pack': + return stripIndent` + pnpm pack + + Creates a compressed gzip archive of package dependencies. + ` + + case 'publish': + return stripIndent` + pnpm publish [|] [--tag ] [--access ] + + Publishes a package to the npm registry. + ` + case 'install-test': return stripIndent` pnpm install-test @@ -473,7 +487,9 @@ function getHelpText (command: string) { - link - list - outdated + - pack - prune + - publish - rebuild - restart - root diff --git a/packages/pnpm/src/cmd/index.ts b/packages/pnpm/src/cmd/index.ts index dca31e5742..f815cd2430 100644 --- a/packages/pnpm/src/cmd/index.ts +++ b/packages/pnpm/src/cmd/index.ts @@ -6,6 +6,7 @@ import link from './link' import list from './list' import outdated from './outdated' import prune from './prune' +import publish, { pack } from './publish' import rebuild from './rebuild' import recursive from './recursive' import root from './root' @@ -24,7 +25,9 @@ export default { link, list, outdated, + pack, prune, + publish, rebuild, recursive, restart, diff --git a/packages/pnpm/src/cmd/publish.ts b/packages/pnpm/src/cmd/publish.ts new file mode 100644 index 0000000000..2918729a75 --- /dev/null +++ b/packages/pnpm/src/cmd/publish.ts @@ -0,0 +1,56 @@ +import readImporterManifest from '@pnpm/read-importer-manifest' +import path = require('path') +import rimraf = require('rimraf-then') +import writeJsonFile = require('write-json-file') +import { PnpmOptions } from '../types' +import runNpm from './runNpm' + +export default async function ( + args: string[], + opts: PnpmOptions, + command: string, +) { + if (args.length && args[0].endsWith('.tgz')) { + await runNpm(['publish', ...args]) + return + } + const prefix = args.length && args[0] || process.cwd() + + let _status!: number + await fakeRegularManifest(prefix, async () => { + const { status } = await runNpm(['publish', ...opts.argv.original.slice(1)]) + _status = status + }) + if (_status !== 0) { + process.exit(_status) + } +} + +export async function pack ( + args: string[], + opts: PnpmOptions, + command: string, +) { + let _status!: number + await fakeRegularManifest(opts.prefix, async () => { + const { status } = await runNpm(['pack', ...opts.argv.original.slice(1)]) + _status = status + }) + if (_status !== 0) { + process.exit(_status) + } +} + +async function fakeRegularManifest (prefix: string, fn: () => Promise) { + const { fileName, manifest, writeImporterManifest } = await readImporterManifest(prefix) + const exoticManifestFormat = fileName !== 'package.json' + if (exoticManifestFormat) { + await rimraf(path.join(prefix, fileName)) + await writeJsonFile(path.join(prefix, 'package.json'), manifest) + } + await fn() + if (exoticManifestFormat) { + await rimraf(path.join(prefix, 'package.json')) + await writeImporterManifest(manifest, true) + } +} diff --git a/packages/pnpm/src/cmd/runNpm.ts b/packages/pnpm/src/cmd/runNpm.ts index fa17b607ea..45ecd73187 100644 --- a/packages/pnpm/src/cmd/runNpm.ts +++ b/packages/pnpm/src/cmd/runNpm.ts @@ -1,10 +1,9 @@ import { sync as runScriptSync } from '../runScript' export default function runNpm (args: string[]) { - const result = runScriptSync('npm', args, { + return runScriptSync('npm', args, { cwd: process.cwd(), stdio: 'inherit', userAgent: undefined, }) - process.exit(result.status) } diff --git a/packages/pnpm/src/main.ts b/packages/pnpm/src/main.ts index 095e0cddaf..a82a91e9c8 100644 --- a/packages/pnpm/src/main.ts +++ b/packages/pnpm/src/main.ts @@ -37,7 +37,9 @@ type CANONICAL_COMMAND_NAMES = 'help' | 'link' | 'list' | 'outdated' + | 'pack' | 'prune' + | 'publish' | 'rebuild' | 'recursive' | 'restart' @@ -59,7 +61,9 @@ const supportedCmds = new Set([ 'uninstall', 'update', 'link', + 'pack', 'prune', + 'publish', 'install-test', 'restart', 'server', diff --git a/packages/pnpm/src/readImporterManifest.ts b/packages/pnpm/src/readImporterManifest.ts deleted file mode 100644 index 8bf9545658..0000000000 --- a/packages/pnpm/src/readImporterManifest.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ImporterManifest } from '@pnpm/types' -import loadJsonFile = require('load-json-file') -import path = require('path') - -export async function readImporterManifest (filename: string) { - return loadJsonFile(filename) -} - -export async function readImporterManifestFromDir (dir: string) { - return readImporterManifest(path.join(dir, 'package.json')) -} - -export async function safeReadImporterManifest (filename: string): Promise { - try { - return await readImporterManifest(filename) - } catch (err) { - if (err['code'] !== 'ENOENT') throw err - return null - } -} - -export function safeReadImporterManifestFromDir (dir: string) { - return safeReadImporterManifest(path.join(dir, 'package.json')) -} diff --git a/packages/pnpm/test/index.ts b/packages/pnpm/test/index.ts index 828ec2bad4..69d834dcdd 100644 --- a/packages/pnpm/test/index.ts +++ b/packages/pnpm/test/index.ts @@ -6,7 +6,9 @@ import './link' import './list' import './monorepo' import './outdated' +import './pack' import './prune' +import './publish' import './rebuild' import './recursive' import './root' diff --git a/packages/pnpm/test/pack.ts b/packages/pnpm/test/pack.ts new file mode 100644 index 0000000000..b34841989a --- /dev/null +++ b/packages/pnpm/test/pack.ts @@ -0,0 +1,49 @@ +import prepare, { + prepareWithYamlManifest, + prepareWithJson5Manifest, +} from '@pnpm/prepare' +import exists = require('path-exists') +import tape = require('tape') +import promisifyTape from 'tape-promise' +import { execPnpm } from './utils' + +const test = promisifyTape(tape) +const testOnly = promisifyTape(tape.only) + +test('pack: package with package.json', async (t: tape.Test) => { + prepare(t, { + name: 'test-publish-package.json', + version: '0.0.0', + }) + + await execPnpm('pack') + + t.ok(await exists('test-publish-package.json-0.0.0.tgz')) + t.ok(await exists('package.json')) +}) + +test('pack: package with package.yaml', async (t: tape.Test) => { + prepareWithYamlManifest(t, { + name: 'test-publish-package.yaml', + version: '0.0.0', + }) + + await execPnpm('pack') + + t.ok(await exists('test-publish-package.yaml-0.0.0.tgz')) + t.ok(await exists('package.yaml')) + t.notOk(await exists('package.json')) +}) + +test('pack: package with package.json5', async (t: tape.Test) => { + prepareWithJson5Manifest(t, { + name: 'test-publish-package.json5', + version: '0.0.0', + }) + + await execPnpm('pack') + + t.ok(await exists('test-publish-package.json5-0.0.0.tgz')) + t.ok(await exists('package.json5')) + t.notOk(await exists('package.json')) +}) diff --git a/packages/pnpm/test/publish.ts b/packages/pnpm/test/publish.ts new file mode 100644 index 0000000000..779f50bb92 --- /dev/null +++ b/packages/pnpm/test/publish.ts @@ -0,0 +1,64 @@ +import prepare, { + prepareWithYamlManifest, + prepareWithJson5Manifest, +} from '@pnpm/prepare' +import exists = require('path-exists') +import tape = require('tape') +import promisifyTape from 'tape-promise' +import { execPnpm } from './utils' + +const test = promisifyTape(tape) +const testOnly = promisifyTape(tape.only) + +const CREDENTIALS = [ + '--//localhost:4873/:username=username', + `--//localhost:4873/:_password=${Buffer.from('password').toString('base64')}`, + '--//localhost:4873/:email=foo@bar.net', +] + +test('publish: package with package.json', async (t: tape.Test) => { + prepare(t, { + name: 'test-publish-package.json', + version: '0.0.0', + }) + + await execPnpm('publish', ...CREDENTIALS) +}) + +test('publish: package with package.yaml', async (t: tape.Test) => { + prepareWithYamlManifest(t, { + name: 'test-publish-package.yaml', + version: '0.0.0', + }) + + await execPnpm('publish', ...CREDENTIALS) + + t.ok(await exists('package.yaml')) + t.notOk(await exists('package.json')) +}) + +test('publish: package with package.json5', async (t: tape.Test) => { + prepareWithJson5Manifest(t, { + name: 'test-publish-package.json5', + version: '0.0.0', + }) + + await execPnpm('publish', ...CREDENTIALS) + + t.ok(await exists('package.json5')) + t.notOk(await exists('package.json')) +}) + +test('publish: package with package.json5 running publish from different folder', async (t: tape.Test) => { + prepareWithJson5Manifest(t, { + name: 'test-publish-package.json5', + version: '0.0.1', + }) + + process.chdir('..') + + await execPnpm('publish', 'project', ...CREDENTIALS) + + t.ok(await exists('project/package.json5')) + t.notOk(await exists('project/package.json')) +}) diff --git a/packages/read-importer-manifest/src/index.ts b/packages/read-importer-manifest/src/index.ts index bd03637e0b..9ddd80bb34 100644 --- a/packages/read-importer-manifest/src/index.ts +++ b/packages/read-importer-manifest/src/index.ts @@ -14,15 +14,19 @@ import { const stat = promisify(fs.stat) +type WriteImporterManifest = (manifest: ImporterManifest, force?: boolean) => Promise + export default async function readImporterManifest (importerDir: string): Promise<{ + fileName: string, manifest: ImporterManifest - writeImporterManifest: (manifest: ImporterManifest) => Promise + writeImporterManifest: WriteImporterManifest }> { const result = await tryReadImporterManifest(importerDir) if (result.manifest !== null) { return result as { + fileName: string, manifest: ImporterManifest - writeImporterManifest: (manifest: ImporterManifest) => Promise + writeImporterManifest: WriteImporterManifest } } const err = new Error(`No package.json (or package.yaml, or package.json5) was found in "${importerDir}".`) @@ -36,14 +40,16 @@ export async function readImporterManifestOnly (importerDir: string): Promise Promise + writeImporterManifest: WriteImporterManifest }> { try { const manifestPath = path.join(importerDir, 'package.json') const { data, text } = await readJsonFile(manifestPath) const { indent } = detectIndent(text) return { + fileName: 'package.json', manifest: data, writeImporterManifest: createManifestWriter({ indent, @@ -59,6 +65,7 @@ export async function tryReadImporterManifest (importerDir: string): Promise<{ const { data, text } = await readJson5File(manifestPath) const { indent } = detectIndent(text) return { + fileName: 'package.json5', manifest: data, writeImporterManifest: createManifestWriter({ indent, @@ -73,6 +80,7 @@ export async function tryReadImporterManifest (importerDir: string): Promise<{ const manifestPath = path.join(importerDir, 'package.yaml') const manifest = await readPackageYaml(manifestPath) return { + fileName: 'package.yaml', manifest, writeImporterManifest: createManifestWriter({ initialManifest: manifest, manifestPath }), } @@ -95,6 +103,7 @@ export async function tryReadImporterManifest (importerDir: string): Promise<{ } const filePath = path.join(importerDir, 'package.json') return { + fileName: 'package.json', manifest: null, writeImporterManifest: writeImporterManifest.bind(null, filePath), } @@ -148,10 +157,10 @@ function createManifestWriter ( indent?: string | number | null | undefined, manifestPath: string, }, -): ((manifest: ImporterManifest) => Promise) { +): (WriteImporterManifest) { const stringifiedInitialManifest = JSON.stringify(opts.initialManifest) - return async (updatedManifest: ImporterManifest) => { - if (stringifiedInitialManifest !== JSON.stringify(updatedManifest)) { + return async (updatedManifest: ImporterManifest, force?: boolean) => { + if (force === true || stringifiedInitialManifest !== JSON.stringify(updatedManifest)) { return writeImporterManifest(opts.manifestPath, updatedManifest, { indent: opts.indent }) } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 630aa9d63d..12602d9ecd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1448,6 +1448,7 @@ importers: tree-kill: 1.2.1 update-notifier: 2.5.0 version-selector-type: 2.0.1 + write-json-file: 3.2.0 devDependencies: '@pnpm/assert-project': 'link:../../privatePackages/assert-project' '@pnpm/lockfile-types': 'link:../lockfile-types' @@ -1489,7 +1490,6 @@ importers: ts-node: 8.1.0_typescript@3.4.5 tslint: 5.16.0_typescript@3.4.5 typescript: 3.4.5 - write-json-file: 3.2.0 write-pkg: 4.0.0 write-yaml-file: 2.0.0 specifiers: