From a955f715d3dfdef2f6b2c026997ba4733caf39f5 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 2 May 2019 00:19:46 +0300 Subject: [PATCH] feat: support package.yaml and package.json5 PR #1799 close #1100 --- packages/find-packages/LICENSE | 21 ++ packages/find-packages/README.md | 51 ++++ packages/find-packages/example.js | 6 + packages/find-packages/package.json | 60 +++++ packages/find-packages/src/index.ts | 57 +++++ .../components/component-1/package.yaml | 2 + .../components/component-2/package.json5 | 4 + .../libs/pkg/package.json | 4 + .../components/component-1/package.json | 4 + .../components/component-2/package.json | 4 + .../fixtures/many-pkgs/libs/pkg/package.json | 4 + .../test/fixtures/one-pkg/pkg/package.json | 4 + packages/find-packages/test/index.ts | 55 ++++ packages/find-packages/tsconfig.json | 10 + packages/find-packages/tslint.json | 3 + packages/find-packages/typings/index.d.ts | 14 + packages/list/package.json | 1 + packages/list/src/index.ts | 7 +- packages/list/src/renderParseable.ts | 4 +- packages/list/src/renderTree.ts | 4 +- packages/local-resolver/package.json | 2 +- packages/local-resolver/src/index.ts | 9 +- packages/local-resolver/test/index.ts | 2 +- packages/outdated/package.json | 2 +- packages/outdated/src/index.ts | 17 +- packages/package-store/typings/index.d.ts | 5 - packages/pkgs-graph/LICENSE | 21 ++ packages/pkgs-graph/README.md | 67 +++++ packages/pkgs-graph/package.json | 48 ++++ packages/pkgs-graph/src/index.ts | 102 ++++++++ packages/pkgs-graph/test/index.ts | 216 ++++++++++++++++ packages/pkgs-graph/tsconfig.json | 10 + packages/pkgs-graph/tslint.json | 3 + packages/pkgs-graph/typings/index.d.ts | 9 + packages/pnpm/package.json | 2 + packages/pnpm/src/cmd/import.ts | 4 +- packages/pnpm/src/cmd/install.ts | 9 +- packages/pnpm/src/cmd/link.ts | 21 +- packages/pnpm/src/cmd/prune.ts | 4 +- packages/pnpm/src/cmd/rebuild.ts | 6 +- packages/pnpm/src/cmd/recursive/exec.ts | 6 +- packages/pnpm/src/cmd/recursive/filter.ts | 22 +- packages/pnpm/src/cmd/recursive/index.ts | 34 ++- packages/pnpm/src/cmd/recursive/run.ts | 18 +- packages/pnpm/src/cmd/uninstall.ts | 15 +- packages/pnpm/src/cmd/unlink.ts | 6 +- packages/pnpm/src/findWorkspacePackages.ts | 4 +- packages/pnpm/test/install/misc.ts | 17 +- packages/pnpm/typings/local.d.ts | 5 - packages/read-importer-manifest/LICENSE | 21 ++ packages/read-importer-manifest/README.md | 25 ++ packages/read-importer-manifest/package.json | 71 ++++++ packages/read-importer-manifest/src/index.ts | 158 ++++++++++++ .../read-importer-manifest/src/readFile.ts | 26 ++ .../test/fixtures/package-json/package.json | 4 + .../test/fixtures/package-json5/package.json5 | 4 + .../test/fixtures/package-yaml/package.yaml | 2 + packages/read-importer-manifest/test/index.ts | 110 ++++++++ packages/read-importer-manifest/tsconfig.json | 11 + packages/read-importer-manifest/tslint.json | 3 + packages/supi/package.json | 1 + packages/supi/src/link/index.ts | 16 +- packages/write-importer-manifest/LICENSE | 21 ++ packages/write-importer-manifest/README.md | 26 ++ packages/write-importer-manifest/package.json | 64 +++++ packages/write-importer-manifest/src/index.ts | 44 ++++ .../write-importer-manifest/test/index.ts | 23 ++ .../write-importer-manifest/tsconfig.json | 11 + packages/write-importer-manifest/tslint.json | 3 + .../typings/index.d.ts | 4 + pnpm-lock.yaml | 239 +++++++++++++++--- 71 files changed, 1745 insertions(+), 147 deletions(-) create mode 100644 packages/find-packages/LICENSE create mode 100644 packages/find-packages/README.md create mode 100644 packages/find-packages/example.js create mode 100644 packages/find-packages/package.json create mode 100644 packages/find-packages/src/index.ts create mode 100644 packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-1/package.yaml create mode 100644 packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-2/package.json5 create mode 100644 packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/libs/pkg/package.json create mode 100644 packages/find-packages/test/fixtures/many-pkgs/components/component-1/package.json create mode 100644 packages/find-packages/test/fixtures/many-pkgs/components/component-2/package.json create mode 100644 packages/find-packages/test/fixtures/many-pkgs/libs/pkg/package.json create mode 100644 packages/find-packages/test/fixtures/one-pkg/pkg/package.json create mode 100644 packages/find-packages/test/index.ts create mode 100644 packages/find-packages/tsconfig.json create mode 100644 packages/find-packages/tslint.json create mode 100644 packages/find-packages/typings/index.d.ts create mode 100644 packages/pkgs-graph/LICENSE create mode 100644 packages/pkgs-graph/README.md create mode 100644 packages/pkgs-graph/package.json create mode 100644 packages/pkgs-graph/src/index.ts create mode 100644 packages/pkgs-graph/test/index.ts create mode 100644 packages/pkgs-graph/tsconfig.json create mode 100644 packages/pkgs-graph/tslint.json create mode 100644 packages/pkgs-graph/typings/index.d.ts create mode 100644 packages/read-importer-manifest/LICENSE create mode 100644 packages/read-importer-manifest/README.md create mode 100644 packages/read-importer-manifest/package.json create mode 100644 packages/read-importer-manifest/src/index.ts create mode 100644 packages/read-importer-manifest/src/readFile.ts create mode 100644 packages/read-importer-manifest/test/fixtures/package-json/package.json create mode 100644 packages/read-importer-manifest/test/fixtures/package-json5/package.json5 create mode 100644 packages/read-importer-manifest/test/fixtures/package-yaml/package.yaml create mode 100644 packages/read-importer-manifest/test/index.ts create mode 100644 packages/read-importer-manifest/tsconfig.json create mode 100644 packages/read-importer-manifest/tslint.json create mode 100644 packages/write-importer-manifest/LICENSE create mode 100644 packages/write-importer-manifest/README.md create mode 100644 packages/write-importer-manifest/package.json create mode 100644 packages/write-importer-manifest/src/index.ts create mode 100644 packages/write-importer-manifest/test/index.ts create mode 100644 packages/write-importer-manifest/tsconfig.json create mode 100644 packages/write-importer-manifest/tslint.json create mode 100644 packages/write-importer-manifest/typings/index.d.ts diff --git a/packages/find-packages/LICENSE b/packages/find-packages/LICENSE new file mode 100644 index 0000000000..04f6d445db --- /dev/null +++ b/packages/find-packages/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Zoltan Kochan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/find-packages/README.md b/packages/find-packages/README.md new file mode 100644 index 0000000000..cb995ea9b3 --- /dev/null +++ b/packages/find-packages/README.md @@ -0,0 +1,51 @@ +# find-packages + +> Find all packages inside a directory + + +[![npm version](https://img.shields.io/npm/v/find-packages.svg)](https://www.npmjs.com/package/find-packages) + + +## Installation + +```sh + add find-packages +``` + +## Usage + +```js +const path = require('path') +const findPkgs = require('find-packages') + +findPkgs(path.join(__dirname, 'test', 'fixture')) + .then(pkgs => console.log(pkgs)) + .catch(err => console.error(err)) + //> [ { path: '/home/zkochan/src/find-packages/test/fixture/pkg', + // manifest: { name: 'foo', version: '1.0.0' }, + // writeImporterManifest: [AsyncFunction] } ] +``` + +## API + +### `findPackages(dir, [opts])` + +#### `dir` + +The directory in which to search for packages. + +#### `opts` + +Parameters normally passed to [glob](https://www.npmjs.com/package/glob) + +#### `opts.patterns` + +Array of globs to use as package locations. For example: `['packages/**', 'utils/**']`. + +#### `opts.ignore` + +Patterns to ignore when searching for packages. By default: `**/node_modules/**`, `**/bower_components/**`, `**/test/**`, `**/tests/**`. + +## License + +[MIT](./LICENSE) © [Zoltan Kochan](https://www.kochan.io) diff --git a/packages/find-packages/example.js b/packages/find-packages/example.js new file mode 100644 index 0000000000..1214141ccb --- /dev/null +++ b/packages/find-packages/example.js @@ -0,0 +1,6 @@ +const path = require('path') +const findPkgs = require('find-packages') + +findPkgs(path.join(__dirname, 'test/fixtures/one-pkg')) + .then(pkgs => console.log(pkgs)) + .catch(err => console.error(err)) diff --git a/packages/find-packages/package.json b/packages/find-packages/package.json new file mode 100644 index 0000000000..97fe123136 --- /dev/null +++ b/packages/find-packages/package.json @@ -0,0 +1,60 @@ +{ + "name": "find-packages", + "version": "4.0.1", + "description": "Find all packages inside a directory", + "main": "lib/index.js", + "files": [ + "lib" + ], + "typings": "lib/index.d.ts", + "scripts": { + "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts", + "test": "npm run tsc && npm run lint && ts-node test --type-check", + "tsc": "tsc", + "prepublishOnly": "npm run tsc", + "md": "mos" + }, + "repository": "https://github.com/pnpm/pnpm/blob/master/packages/find-packages", + "keywords": [ + "find", + "package" + ], + "mos": { + "plugins": [ + "readme" + ], + "installation": { + "useShortAlias": true + } + }, + "author": { + "name": "Zoltan Kochan", + "email": "z@kochan.io", + "url": "https://www.kochan.io" + }, + "engines": { + "node": ">=8.15" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/pnpm/pnpm/issues" + }, + "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/find-packages#readme", + "devDependencies": { + "@pnpm/tslint-config": "0.0.0", + "@types/tape": "^4.2.29", + "find-packages": "link:", + "mos": "^2.0.0-alpha.3", + "mos-plugin-readme": "^1.0.4", + "tape": "^4.6.3", + "ts-node": "^8.0.1", + "tslint": "^5.0.0", + "typescript": "^3.0.0" + }, + "dependencies": { + "@pnpm/read-importer-manifest": "0.0.0", + "@types/node": "^10.12.18", + "fast-glob": "^2.0.4", + "p-filter": "^2.0.0" + } +} diff --git a/packages/find-packages/src/index.ts b/packages/find-packages/src/index.ts new file mode 100644 index 0000000000..aaf3f3433d --- /dev/null +++ b/packages/find-packages/src/index.ts @@ -0,0 +1,57 @@ +import { readExactImporterManifest } from '@pnpm/read-importer-manifest' +import fastGlob = require('fast-glob') +import pFilter = require('p-filter') +import path = require('path') + +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/bower_components/**', + '**/test/**', + '**/tests/**', +] + +async function findPkgs ( + root: string, + opts?: { + ignore?: string[], + patterns?: string[], + }, +) { + opts = opts || {} + const globOpts = { ...opts, cwd: root } + globOpts.ignore = opts.ignore || DEFAULT_IGNORE + globOpts.patterns = opts.patterns + ? normalizePatterns(opts.patterns) + : ['**/package.{json,yaml,json5}'] + + const paths: string[] = await fastGlob(globOpts.patterns, globOpts) + + return pFilter( + paths + .sort() + .map((manifestPath) => path.join(root, manifestPath)) + .map(async (manifestPath) => { + try { + return { + path: path.dirname(manifestPath), + ...await readExactImporterManifest(manifestPath), + } + } catch (err) { + if (err.code === 'ENOENT') { + return null + } + throw err + } + }), + Boolean, + ) +} + +function normalizePatterns (patterns: string[]) { + return patterns.map((pattern) => pattern.replace(/\/?$/, '/package.{json,yaml,json5}')) +} + +// for backward compatibility +findPkgs['default'] = findPkgs // tslint:disable-line + +export = findPkgs diff --git a/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-1/package.yaml b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-1/package.yaml new file mode 100644 index 0000000000..4045b191d2 --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-1/package.yaml @@ -0,0 +1,2 @@ +name: component-1 +version: 1.0.0 \ No newline at end of file diff --git a/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-2/package.json5 b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-2/package.json5 new file mode 100644 index 0000000000..294e187720 --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/components/component-2/package.json5 @@ -0,0 +1,4 @@ +{ + name: 'component-2', + version: '1.0.0', +} diff --git a/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/libs/pkg/package.json b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/libs/pkg/package.json new file mode 100644 index 0000000000..47d28ce069 --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs-with-different-manifest-types/libs/pkg/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "version": "1.0.0" +} \ No newline at end of file diff --git a/packages/find-packages/test/fixtures/many-pkgs/components/component-1/package.json b/packages/find-packages/test/fixtures/many-pkgs/components/component-1/package.json new file mode 100644 index 0000000000..fd4ca668ce --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs/components/component-1/package.json @@ -0,0 +1,4 @@ +{ + "name": "component-1", + "version": "1.0.0" +} \ No newline at end of file diff --git a/packages/find-packages/test/fixtures/many-pkgs/components/component-2/package.json b/packages/find-packages/test/fixtures/many-pkgs/components/component-2/package.json new file mode 100644 index 0000000000..263991e12b --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs/components/component-2/package.json @@ -0,0 +1,4 @@ +{ + "name": "component-2", + "version": "1.0.0" +} \ No newline at end of file diff --git a/packages/find-packages/test/fixtures/many-pkgs/libs/pkg/package.json b/packages/find-packages/test/fixtures/many-pkgs/libs/pkg/package.json new file mode 100644 index 0000000000..47d28ce069 --- /dev/null +++ b/packages/find-packages/test/fixtures/many-pkgs/libs/pkg/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "version": "1.0.0" +} \ No newline at end of file diff --git a/packages/find-packages/test/fixtures/one-pkg/pkg/package.json b/packages/find-packages/test/fixtures/one-pkg/pkg/package.json new file mode 100644 index 0000000000..47d28ce069 --- /dev/null +++ b/packages/find-packages/test/fixtures/one-pkg/pkg/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "version": "1.0.0" +} \ No newline at end of file diff --git a/packages/find-packages/test/index.ts b/packages/find-packages/test/index.ts new file mode 100644 index 0000000000..80e31ef316 --- /dev/null +++ b/packages/find-packages/test/index.ts @@ -0,0 +1,55 @@ +import findPackages from 'find-packages' +import path = require('path') +import test = require('tape') + +const fixtures = path.join(__dirname, 'fixtures') + +test('finds package', async t => { + const root = path.join(fixtures, 'one-pkg') + const pkgs = await findPackages(root) + + t.equal(pkgs.length, 1) + t.ok(pkgs[0].path) + t.ok(pkgs[0].manifest) + t.end() +}) + +test('finds packages by patterns', async t => { + const root = path.join(fixtures, 'many-pkgs') + const pkgs = await findPackages(root, { patterns: ['components/**'] }) + + t.equal(pkgs.length, 2) + t.ok(pkgs[0].path) + t.ok(pkgs[0].manifest) + t.ok(pkgs[1].path) + t.ok(pkgs[1].manifest) + t.deepEqual([pkgs[0].manifest.name, pkgs[1].manifest.name].sort(), ['component-1', 'component-2']) + t.end() +}) + +test('ignore packages by patterns', async t => { + const root = path.join(fixtures, 'many-pkgs') + const pkgs = await findPackages(root, { patterns: ['**', '!libs/**'] }) + + t.equal(pkgs.length, 2) + t.ok(pkgs[0].path) + t.ok(pkgs[0].manifest) + t.ok(pkgs[1].path) + t.ok(pkgs[1].manifest) + t.deepEqual([pkgs[0].manifest.name, pkgs[1].manifest.name].sort(), ['component-1', 'component-2']) + t.end() +}) + +test('json and yaml manifests are also found', async t => { + const root = path.join(fixtures, 'many-pkgs-with-different-manifest-types') + const pkgs = await findPackages(root) + + t.equal(pkgs.length, 3) + t.ok(pkgs[0].path) + t.equal(pkgs[0].manifest.name, 'component-1') + t.ok(pkgs[1].path) + t.equal(pkgs[1].manifest.name, 'component-2') + t.ok(pkgs[2].path) + t.equal(pkgs[2].manifest.name, 'foo') + t.end() +}) diff --git a/packages/find-packages/tsconfig.json b/packages/find-packages/tsconfig.json new file mode 100644 index 0000000000..7d9e0c3e59 --- /dev/null +++ b/packages/find-packages/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../utils/tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "src/**/*.ts", + "typings/**/*.d.ts" + ] +} diff --git a/packages/find-packages/tslint.json b/packages/find-packages/tslint.json new file mode 100644 index 0000000000..568acfabec --- /dev/null +++ b/packages/find-packages/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@pnpm/tslint-config" +} diff --git a/packages/find-packages/typings/index.d.ts b/packages/find-packages/typings/index.d.ts new file mode 100644 index 0000000000..68cdde2fd0 --- /dev/null +++ b/packages/find-packages/typings/index.d.ts @@ -0,0 +1,14 @@ +declare module 'fast-glob' { + const anything: any; + export = anything; +} + +declare module 'read-pkg' { + const anything: any; + export = anything; +} + +declare module 'p-filter' { + const anything: any; + export = anything; +} diff --git a/packages/list/package.json b/packages/list/package.json index 1aeeb82374..2142efbb16 100644 --- a/packages/list/package.json +++ b/packages/list/package.json @@ -36,6 +36,7 @@ }, "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/list#readme", "dependencies": { + "@pnpm/read-importer-manifest": "0.0.0", "@pnpm/read-package-json": "2.0.1", "@pnpm/types": "3.2.0", "@types/archy": "0.0.31", diff --git a/packages/list/src/index.ts b/packages/list/src/index.ts index 3bb0f02711..97301fb041 100644 --- a/packages/list/src/index.ts +++ b/packages/list/src/index.ts @@ -1,11 +1,10 @@ +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import { Registries } from '@pnpm/types' import npa = require('@zkochan/npm-package-arg') import dh, { forPackages as dhForPackages, PackageSelector, } from 'dependencies-hierarchy' -import path = require('path') -import readPkg from './readPkg' import renderParseable from './renderParseable' import renderTree from './renderTree' @@ -55,7 +54,7 @@ export async function forPackages ( }) const print = getPrinter(opts.parseable) - const entryPkg = await readPkg(path.resolve(projectPath, 'package.json')) + const entryPkg = await readImporterManifestOnly(projectPath) return print({ name: entryPkg.name, path: projectPath, @@ -90,7 +89,7 @@ export default async function ( }) const print = getPrinter(opts.parseable) - const entryPkg = await readPkg(path.resolve(projectPath, 'package.json')) + const entryPkg = await readImporterManifestOnly(projectPath) return print({ name: entryPkg.name, path: projectPath, diff --git a/packages/list/src/renderParseable.ts b/packages/list/src/renderParseable.ts index bb3ca00b29..317e412dde 100644 --- a/packages/list/src/renderParseable.ts +++ b/packages/list/src/renderParseable.ts @@ -5,8 +5,8 @@ const sortPackages = R.sortBy(R.prop('name')) export default async function ( project: { - name: string, - version: string, + name?: string, + version?: string, path: string, }, tree: PackageNode[], diff --git a/packages/list/src/renderTree.ts b/packages/list/src/renderTree.ts index 3da2e9d196..17330b039f 100644 --- a/packages/list/src/renderTree.ts +++ b/packages/list/src/renderTree.ts @@ -9,8 +9,8 @@ const sortPackages = R.sortBy(R.path(['pkg', 'name']) as (pkg: object) => R.Ord) export default async function ( project: { - name: string, - version: string, + name?: string, + version?: string, path: string, }, tree: PackageNode[], diff --git a/packages/local-resolver/package.json b/packages/local-resolver/package.json index 5e6f9846aa..9fd3ba4d98 100644 --- a/packages/local-resolver/package.json +++ b/packages/local-resolver/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/local-resolver#readme", "dependencies": { - "@pnpm/read-package-json": "2.0.1", + "@pnpm/read-importer-manifest": "0.0.0", "@pnpm/resolver-base": "3.1.2", "@pnpm/types": "3.2.0", "@types/graceful-fs": "4.1.3", diff --git a/packages/local-resolver/src/index.ts b/packages/local-resolver/src/index.ts index a85389ae82..97561cf8de 100644 --- a/packages/local-resolver/src/index.ts +++ b/packages/local-resolver/src/index.ts @@ -1,4 +1,4 @@ -import readPackageJson from '@pnpm/read-package-json' +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import { DirectoryResolution, ResolveResult, @@ -6,7 +6,6 @@ import { } from '@pnpm/resolver-base' import { DependencyManifest } from '@pnpm/types' import fs = require('graceful-fs') -import path = require('path') import ssri = require('ssri') import parsePref from './parsePref' @@ -44,9 +43,9 @@ export default async function resolveLocal ( } } - let localPkg!: DependencyManifest + let localDependencyManifest!: DependencyManifest try { - localPkg = await readPackageJson(path.join(spec.fetchSpec, 'package.json')) as DependencyManifest + localDependencyManifest = await readImporterManifestOnly(spec.fetchSpec) as DependencyManifest } catch (internalErr) { switch (internalErr.code) { case 'ENOTDIR': { @@ -67,7 +66,7 @@ export default async function resolveLocal ( return { id: spec.id, normalizedPref: spec.normalizedPref, - package: localPkg, + package: localDependencyManifest, resolution: { directory: spec.dependencyPath, type: 'directory', diff --git a/packages/local-resolver/test/index.ts b/packages/local-resolver/test/index.ts index c5fd6a89d7..7c4a20edb5 100644 --- a/packages/local-resolver/test/index.ts +++ b/packages/local-resolver/test/index.ts @@ -105,7 +105,7 @@ test('fail when resolving from not existing directory', async t => { t.fail() } catch (err) { t.ok(err) - t.equal(err.code, 'ERR_PNPM_DIRECTORY_HAS_NO_PACKAGE_JSON') + t.equal(err.code, 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND') t.end() } }) diff --git a/packages/outdated/package.json b/packages/outdated/package.json index 6ad404f4c5..cabfd9e4e1 100644 --- a/packages/outdated/package.json +++ b/packages/outdated/package.json @@ -43,7 +43,7 @@ "@pnpm/constants": "1.0.0", "@pnpm/lockfile-file": "1.0.4", "@pnpm/npm-resolver": "3.0.7", - "@pnpm/read-package-json": "2.0.1", + "@pnpm/read-importer-manifest": "0.0.0", "@pnpm/store-path": "1.0.4", "@pnpm/types": "3.2.0", "@pnpm/utils": "0.10.4", diff --git a/packages/outdated/src/index.ts b/packages/outdated/src/index.ts index d157c9979f..e15735522c 100644 --- a/packages/outdated/src/index.ts +++ b/packages/outdated/src/index.ts @@ -5,9 +5,9 @@ import { readWantedLockfile, } from '@pnpm/lockfile-file' import createResolver from '@pnpm/npm-resolver' -import { fromDir as readPackageFromDir } from '@pnpm/read-package-json' +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import resolveStore from '@pnpm/store-path' -import { DEPENDENCIES_FIELDS, Registries } from '@pnpm/types' +import { DEPENDENCIES_FIELDS, ImporterManifest, Registries } from '@pnpm/types' import { normalizeRegistries } from '@pnpm/utils' import * as dp from 'dependency-path' @@ -103,8 +103,8 @@ async function _outdated ( ): Promise { const registries = normalizeRegistries(opts.registries) const lockfileDirectory = opts.lockfileDirectory || pkgPath - const pkg = await readPackageFromDir(pkgPath) - if (packageHasNoDeps(pkg)) return [] + const manifest = await readImporterManifestOnly(pkgPath) + if (packageHasNoDeps(manifest)) return [] const wantedLockfile = await readWantedLockfile(lockfileDirectory, { ignoreIncompatible: false }) || await readCurrentLockfile(lockfileDirectory, { ignoreIncompatible: false }) if (!wantedLockfile) { @@ -211,11 +211,10 @@ async function _outdated ( return outdated.sort((pkg1, pkg2) => pkg1.packageName.localeCompare(pkg2.packageName)) } -// tslint:disable-next-line:no-any -function packageHasNoDeps (pkg: any) { - return (!pkg.dependencies || isEmpty(pkg.dependencies)) - && (!pkg.devDependencies || isEmpty(pkg.devDependencies)) - && (!pkg.optionalDependencies || isEmpty(pkg.optionalDependencies)) +function packageHasNoDeps (manifest: ImporterManifest) { + return (!manifest.dependencies || isEmpty(manifest.dependencies)) + && (!manifest.devDependencies || isEmpty(manifest.devDependencies)) + && (!manifest.optionalDependencies || isEmpty(manifest.optionalDependencies)) } function isEmpty (obj: object) { diff --git a/packages/package-store/typings/index.d.ts b/packages/package-store/typings/index.d.ts index 672810109b..0bfbef4038 100644 --- a/packages/package-store/typings/index.d.ts +++ b/packages/package-store/typings/index.d.ts @@ -108,11 +108,6 @@ declare module '@zkochan/cmd-shim' { export = anything; } -declare module 'write-pkg' { - const anything: any; - export = anything; -} - declare module 'is-windows' { function isWindows(): boolean; export = isWindows; diff --git a/packages/pkgs-graph/LICENSE b/packages/pkgs-graph/LICENSE new file mode 100644 index 0000000000..c9ac620c74 --- /dev/null +++ b/packages/pkgs-graph/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-present Zoltan Kochan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/pkgs-graph/README.md b/packages/pkgs-graph/README.md new file mode 100644 index 0000000000..88e097e70c --- /dev/null +++ b/packages/pkgs-graph/README.md @@ -0,0 +1,67 @@ +# pkgs-graph + +> Create a graph from an array of packages + +[![npm version](https://img.shields.io/npm/v/pkgs-graph.svg)](https://www.npmjs.com/package/pkgs-graph) + +## Installation + +``` + add pkgs-graph +``` + +## Usage + +```js +import createPkgsGraph from 'pkgs-graph' + +const {graph} = createPkgsGraph([ + { + manifest: { + name: 'foo', + version: '1.0.0', + dependencies: { + bar: '^1.0.0', + }, + }, + path: '/home/zkochan/src/foo', + }, + { + manifest: { + name: 'bar', + version: '1.1.0', + }, + path: '/home/zkochan/src/bar', + } +]) + +console.log(graph) +//> { +// '/home/zkochan/src/foo': { +// dependencies: ['/home/zkochan/src/bar'], +// manifest: { +// name: 'foo', +// version: '1.0.0', +// dependencies: { +// bar: '^1.0.0', +// }, +// }, +// }, +// '/home/zkochan/src/bar': { +// dependencies: [], +// manifest: { +// name: 'bar', +// version: '1.1.0', +// }, +// }, +// } +``` + +## Related + +* [find-packages](https://github.com/zkochan/find-packages) - Find all packages inside a directory +* [sort-pkgs](https://github.com/zkochan/sort-pkgs) - Sort packages. Dependents first. + +## License + +[MIT](LICENSE) © [Zoltan Kochan](https://www.kochan.io) diff --git a/packages/pkgs-graph/package.json b/packages/pkgs-graph/package.json new file mode 100644 index 0000000000..f4c20ae98f --- /dev/null +++ b/packages/pkgs-graph/package.json @@ -0,0 +1,48 @@ +{ + "name": "pkgs-graph", + "version": "3.0.0", + "description": "Create a graph from an array of packages", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts", + "test": "npm run tsc && pnpm run lint && ts-node test --type-check", + "tsc": "tsc", + "prepublishOnly": "npm run tsc" + }, + "repository": "https://github.com/pnpm/pnpm/blob/master/packages/pkgs-graph", + "author": { + "name": "Zoltan Kochan", + "email": "z@kochan.io", + "url": "https://www.kochan.io/" + }, + "license": "MIT", + "engines": { + "node": ">=8.15" + }, + "bugs": { + "url": "https://github.com/pnpm/pnpm/issues" + }, + "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/pkgs-graph#readme", + "devDependencies": { + "@pnpm/tslint-config": "0.0.0", + "@types/tape": "^4.2.29", + "better-path-resolve": "1.0.0", + "pkgs-graph": "link:", + "tape": "^4.6.3", + "ts-node": "^8.0.3", + "tslint": "5.16.0", + "typescript": "^3.0.0" + }, + "dependencies": { + "@types/node": "*", + "@types/ramda": "^0.26.0", + "@types/semver": "^5.3.31", + "@zkochan/npm-package-arg": "^1.0.0", + "ramda": "^0.26.0", + "semver": "^6.0.0" + } +} diff --git a/packages/pkgs-graph/src/index.ts b/packages/pkgs-graph/src/index.ts new file mode 100644 index 0000000000..9382441f27 --- /dev/null +++ b/packages/pkgs-graph/src/index.ts @@ -0,0 +1,102 @@ +/// +import npa = require('@zkochan/npm-package-arg') +import path = require('path') +import R = require('ramda') +import semver = require('semver') + +export type Manifest = { + name: string, + version: string, + dependencies?: { + [name: string]: string, + }, + devDependencies?: { + [name: string]: string, + }, + optionalDependencies?: { + [name: string]: string, + }, +} + +export type Package = { + manifest: Manifest, + path: string, +} + +export type PackageNode = { + package: Package & T, + dependencies: string[], +} + +export default function (pkgs: Array): { + graph: {[id: string]: PackageNode}, + unmatched: Array<{pkgName: string, range: string}>, +} { + const pkgMap = createPkgMap(pkgs) + const unmatched: Array<{pkgName: string, range: string}> = [] + const graph = Object.keys(pkgMap) + .reduce((acc, pkgSpec) => { + acc[pkgSpec] = { + dependencies: createNode(pkgMap[pkgSpec]), + package: pkgMap[pkgSpec], + } + return acc + }, {}) + + return { graph, unmatched } + + function createNode (pkg: Package): string[] { + const dependencies = Object.assign({}, + pkg.manifest.devDependencies, + pkg.manifest.optionalDependencies, + pkg.manifest.dependencies) + + return Object.keys(dependencies) + .map(depName => { + let spec!: { fetchSpec: string, type: string } + try { + spec = npa.resolve(depName, dependencies[depName], pkg.path) + } catch (err) { + return '' + } + + if (spec.type === 'directory') { + const matchedPkg = R.values(pkgMap).find(pkg => path.relative(pkg.path, spec.fetchSpec) === '') + if (!matchedPkg) { + return '' + } + return matchedPkg!.path + } + + if (spec.type !== 'version' && spec.type !== 'range') return '' + + const range = dependencies[depName] + + const pkgs = R.values(pkgMap).filter(pkg => pkg.manifest.name === depName) + if (!pkgs.length) return '' + const versions = pkgs.map(pkg => pkg.manifest.version) + if (versions.indexOf(range) !== -1) { + const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName && pkg.manifest.version === range) + return matchedPkg!.path + } + const matched = semver.maxSatisfying(versions, range) + if (!matched) { + unmatched.push({ pkgName: depName, range }) + return '' + } + const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName && pkg.manifest.version === matched) + return matchedPkg!.path + }) + .filter(Boolean) + } +} + +function createPkgMap (pkgs: Package[]): { + [pkgId: string]: Package +} { + const pkgMap = {} + for (let pkg of pkgs) { + pkgMap[pkg.path] = pkg + } + return pkgMap +} diff --git a/packages/pkgs-graph/test/index.ts b/packages/pkgs-graph/test/index.ts new file mode 100644 index 0000000000..99cc363f2e --- /dev/null +++ b/packages/pkgs-graph/test/index.ts @@ -0,0 +1,216 @@ +import pathResolve = require('better-path-resolve') +import createPkgGraph from 'pkgs-graph' +import test = require('tape') + +const BAR1_PATH = pathResolve('/zkochan/src/bar') +const FOO1_PATH = pathResolve('/zkochan/src/foo') +const BAR2_PATH = pathResolve('/zkochan/src/bar@2') +const FOO2_PATH = pathResolve('/zkochan/src/foo@2') + +test('create package graph', t => { + const result = createPkgGraph([ + { + manifest: { + name: 'bar', + version: '1.0.0', + + dependencies: { + 'foo': '^1.0.0', + 'is-positive': '1.0.0', + } + }, + path: BAR1_PATH, + }, + { + manifest: { + name: 'foo', + version: '1.0.0', + + dependencies: { + bar: '^10.0.0' + } + }, + path: FOO1_PATH, + }, + { + manifest: { + name: 'bar', + version: '2.0.0', + + dependencies: { + foo: '^2.0.0' + } + }, + path: BAR2_PATH, + }, + { + manifest: { + name: 'foo', + version: '2.0.0', + }, + path: FOO2_PATH, + }, + ]) + t.deepEqual(result.unmatched, [{ pkgName: 'bar', range: '^10.0.0' }]) + t.deepEqual(result.graph, { + [BAR1_PATH]: { + dependencies: [FOO1_PATH], + package: { + manifest: { + name: 'bar', + version: '1.0.0', + + dependencies: { + 'foo': '^1.0.0', + 'is-positive': '1.0.0', + } + }, + path: BAR1_PATH, + }, + }, + [FOO1_PATH]: { + dependencies: [], + package: { + manifest: { + name: 'foo', + version: '1.0.0', + + dependencies: { + bar: '^10.0.0' + } + }, + path: FOO1_PATH, + }, + }, + [BAR2_PATH]: { + dependencies: [FOO2_PATH], + package: { + manifest: { + name: 'bar', + version: '2.0.0', + + dependencies: { + foo: '^2.0.0', + }, + }, + path: BAR2_PATH, + }, + }, + [FOO2_PATH]: { + dependencies: [], + package: { + manifest: { + name: 'foo', + version: '2.0.0', + }, + path: FOO2_PATH, + }, + }, + }) + t.end() +}) + +test('create package graph for local directory dependencies', t => { + const result = createPkgGraph([ + { + manifest: { + name: 'bar', + version: '1.0.0', + + dependencies: { + 'foo': '../foo', + 'is-positive': '1.0.0', + 'weird-dep': ':aaaaa', // weird deps are skipped + }, + }, + path: BAR1_PATH, + }, + { + manifest: { + name: 'foo', + version: '1.0.0', + + dependencies: { + bar: '^10.0.0', + }, + }, + path: FOO1_PATH, + }, + { + manifest: { + name: 'bar', + version: '2.0.0', + + dependencies: { + foo: 'file:../foo@2', + }, + }, + path: BAR2_PATH, + }, + { + manifest: { + name: 'foo', + version: '2.0.0', + }, + path: FOO2_PATH, + }, + ]) + t.deepEqual(result.unmatched, [{ pkgName: 'bar', range: '^10.0.0' }]) + t.deepEqual(result.graph, { + [BAR1_PATH]: { + dependencies: [FOO1_PATH], + package: { + manifest: { + name: 'bar', + version: '1.0.0', + + dependencies: { + 'foo': '../foo', + 'is-positive': '1.0.0', + 'weird-dep': ':aaaaa', + }, + }, + path: BAR1_PATH, + }, + }, + [FOO1_PATH]: { + dependencies: [], + package: { + manifest: { + name: 'foo', + version: '1.0.0', + + dependencies: { + bar: '^10.0.0', + }, + }, + path: FOO1_PATH, + }, + }, + [BAR2_PATH]: { + dependencies: [FOO2_PATH], + package: { + manifest: { + name: 'bar', + version: '2.0.0', + + dependencies: { + foo: 'file:../foo@2' + }, + }, + path: BAR2_PATH, + }, + }, + [FOO2_PATH]: { + dependencies: [], + package: { + manifest: { + name: 'foo', + version: '2.0.0', + }, + path: FOO2_PATH, + }, + }, + }) + t.end() +}) diff --git a/packages/pkgs-graph/tsconfig.json b/packages/pkgs-graph/tsconfig.json new file mode 100644 index 0000000000..7d9e0c3e59 --- /dev/null +++ b/packages/pkgs-graph/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../utils/tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "src/**/*.ts", + "typings/**/*.d.ts" + ] +} diff --git a/packages/pkgs-graph/tslint.json b/packages/pkgs-graph/tslint.json new file mode 100644 index 0000000000..568acfabec --- /dev/null +++ b/packages/pkgs-graph/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@pnpm/tslint-config" +} diff --git a/packages/pkgs-graph/typings/index.d.ts b/packages/pkgs-graph/typings/index.d.ts new file mode 100644 index 0000000000..f4958ae8a5 --- /dev/null +++ b/packages/pkgs-graph/typings/index.d.ts @@ -0,0 +1,9 @@ +declare module '@zkochan/npm-package-arg' { + const anything: any; + export = anything; +} + +declare module 'better-path-resolve' { + const anything: any; + export = anything; +} diff --git a/packages/pnpm/package.json b/packages/pnpm/package.json index 9eb781e6b4..8003c0eedb 100644 --- a/packages/pnpm/package.json +++ b/packages/pnpm/package.json @@ -32,6 +32,7 @@ "@pnpm/logger": "2.1.0", "@pnpm/outdated": "2.0.8", "@pnpm/package-store": "4.0.6", + "@pnpm/read-importer-manifest": "0.0.0", "@pnpm/server": "3.0.3", "@pnpm/store-controller-types": "3.0.3", "@pnpm/store-path": "1.0.4", @@ -92,6 +93,7 @@ "@pnpm/prepare": "0.0.0", "@pnpm/read-package-json": "2.0.1", "@pnpm/tslint-config": "0.0.0", + "@pnpm/write-importer-manifest": "0.0.0", "@types/byline": "4.2.31", "@types/common-tags": "1.8.0", "@types/mkdirp": "0.5.2", diff --git a/packages/pnpm/src/cmd/import.ts b/packages/pnpm/src/cmd/import.ts index 06e8d7335b..b7fffc195b 100644 --- a/packages/pnpm/src/cmd/import.ts +++ b/packages/pnpm/src/cmd/import.ts @@ -1,10 +1,10 @@ import { WANTED_LOCKFILE } from '@pnpm/constants' +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import loadJsonFile = require('load-json-file') import path = require('path') import rimraf = require('rimraf-then') import { install } from 'supi' import createStoreController from '../createStoreController' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' export default async function installCmd ( @@ -26,7 +26,7 @@ export default async function installCmd ( store: store.path, storeController: store.ctrl, } - await install(await readImporterManifestFromDir(opts.prefix), installOpts) + await install(await readImporterManifestOnly(opts.prefix), installOpts) } async function readNpmLockfile (prefix: string) { diff --git a/packages/pnpm/src/cmd/install.ts b/packages/pnpm/src/cmd/install.ts index a863e25d1d..860155a6f1 100644 --- a/packages/pnpm/src/cmd/install.ts +++ b/packages/pnpm/src/cmd/install.ts @@ -1,14 +1,13 @@ +import { readImporterManifestOnly, tryReadImporterManifest } from '@pnpm/read-importer-manifest' import { getSaveType } from '@pnpm/utils' import { install, mutateModules, rebuild, } from 'supi' -import writePkg = require('write-pkg') import createStoreController from '../createStoreController' import findWorkspacePackages, { arrayOfLocalPackagesToMap } from '../findWorkspacePackages' import getPinnedVersion from '../getPinnedVersion' -import { readImporterManifestFromDir, safeReadImporterManifestFromDir } from '../readImporterManifest' import requireHooks from '../requireHooks' import { PnpmOptions } from '../types' import updateToLatestSpecsFromManifest, { createLatestSpecs } from '../updateToLatestSpecsFromManifest' @@ -54,7 +53,7 @@ export default async function installCmd ( storeController: store.ctrl, } - let manifest = await safeReadImporterManifestFromDir(opts.prefix) + let { manifest, writeImporterManifest } = await tryReadImporterManifest(opts.prefix) if (manifest === null) { if (opts.update) { const err = new Error('No package.json found') @@ -87,7 +86,7 @@ export default async function installCmd ( targetDependenciesField: getSaveType(installOpts), }, ], installOpts) - await writePkg(opts.prefix, updatedImporter.manifest) + await writeImporterManifest(updatedImporter.manifest) } if (opts.linkWorkspacePackages && opts.workspacePrefix) { @@ -113,7 +112,7 @@ export default async function installCmd ( [ { buildIndex: 0, - manifest: await readImporterManifestFromDir(opts.prefix), + manifest: await readImporterManifestOnly(opts.prefix), prefix: opts.prefix, }, ], { diff --git a/packages/pnpm/src/cmd/link.ts b/packages/pnpm/src/cmd/link.ts index 1795e68884..052f5a47c0 100644 --- a/packages/pnpm/src/cmd/link.ts +++ b/packages/pnpm/src/cmd/link.ts @@ -1,5 +1,8 @@ import { StoreController } from '@pnpm/package-store' -import { safeReadPackageFromDir } from '@pnpm/utils' +import readImporterManifest, { + readImporterManifestOnly, + tryReadImporterManifest, +} from '@pnpm/read-importer-manifest' import pLimit = require('p-limit') import path = require('path') import pathAbsolute = require('path-absolute') @@ -11,11 +14,9 @@ import { linkToGlobal, LocalPackages, } from 'supi' -import writePkg = require('write-pkg') import { cached as createStoreController } from '../createStoreController' import findWorkspacePackages, { arrayOfLocalPackagesToMap } from '../findWorkspacePackages' import getConfigs from '../getConfigs' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' const installLimit = pLimit(4) @@ -45,12 +46,12 @@ export default async ( // pnpm link if (!input || !input.length) { - const manifest = await safeReadPackageFromDir(opts.globalPrefix) || {} + const { manifest, writeImporterManifest } = await tryReadImporterManifest(opts.globalPrefix) const newManifest = await linkToGlobal(cwd, { ...linkOpts, - manifest, + manifest: manifest || {}, }) - await writePkg(opts.globalPrefix, newManifest) + await writeImporterManifest(newManifest) return } @@ -80,7 +81,7 @@ export default async ( pkgPaths.map((prefix) => installLimit(async () => { const s = await createStoreController(storeControllerCache, opts) await install( - await readImporterManifestFromDir(prefix), { + await readImporterManifestOnly(prefix), { ...await getConfigs( { ...opts.cliArgs, prefix }, { @@ -95,12 +96,12 @@ export default async ( ) })), ) - const currentManifest = await readImporterManifestFromDir(cwd) + const { manifest, writeImporterManifest } = await readImporterManifest(cwd) const newManifest = await link(pkgPaths, path.join(cwd, 'node_modules'), { ...linkOpts, - manifest: currentManifest, + manifest, }) - await writePkg(cwd, newManifest) + await writeImporterManifest(newManifest) await Promise.all( Array.from(storeControllerCache.values()) diff --git a/packages/pnpm/src/cmd/prune.ts b/packages/pnpm/src/cmd/prune.ts index 13355a2625..64c22c03f5 100644 --- a/packages/pnpm/src/cmd/prune.ts +++ b/packages/pnpm/src/cmd/prune.ts @@ -1,6 +1,6 @@ +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import { InstallOptions, mutateModules } from 'supi' import createStoreController from '../createStoreController' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' export default async (input: string[], opts: PnpmOptions) => { @@ -8,7 +8,7 @@ export default async (input: string[], opts: PnpmOptions) => { return mutateModules([ { buildIndex: 0, - manifest: await readImporterManifestFromDir(process.cwd()), + manifest: await readImporterManifestOnly(process.cwd()), mutation: 'install', prefix: process.cwd(), pruneDirectDependencies: true, diff --git a/packages/pnpm/src/cmd/rebuild.ts b/packages/pnpm/src/cmd/rebuild.ts index 89cb82dc23..ef707395b0 100644 --- a/packages/pnpm/src/cmd/rebuild.ts +++ b/packages/pnpm/src/cmd/rebuild.ts @@ -1,9 +1,9 @@ +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import { rebuild, rebuildPkgs, } from 'supi' import createStoreController from '../createStoreController' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' export default async function ( @@ -22,7 +22,7 @@ export default async function ( [ { buildIndex: 0, - manifest: await readImporterManifestFromDir(rebuildOpts.prefix), + manifest: await readImporterManifestOnly(rebuildOpts.prefix), prefix: rebuildOpts.prefix, }, ], @@ -32,7 +32,7 @@ export default async function ( await rebuildPkgs( [ { - manifest: await readImporterManifestFromDir(rebuildOpts.prefix), + manifest: await readImporterManifestOnly(rebuildOpts.prefix), prefix: rebuildOpts.prefix, }, ], diff --git a/packages/pnpm/src/cmd/recursive/exec.ts b/packages/pnpm/src/cmd/recursive/exec.ts index d5cde662f8..1b1e01a6b6 100644 --- a/packages/pnpm/src/cmd/recursive/exec.ts +++ b/packages/pnpm/src/cmd/recursive/exec.ts @@ -4,9 +4,9 @@ import pLimit = require('p-limit') import { PackageNode } from 'pkgs-graph' import RecursiveSummary from './recursiveSummary' -export default async ( +export default async ( packageChunks: string[][], - graph: {[id: string]: PackageNode}, + graph: {[id: string]: PackageNode}, args: string[], cmd: string, opts: { @@ -31,7 +31,7 @@ export default async ( cwd: prefix, env: { ...process.env, - PNPM_PACKAGE_NAME: graph[prefix].manifest.name, + PNPM_PACKAGE_NAME: graph[prefix].package.manifest.name, }, stdio: 'inherit', }) diff --git a/packages/pnpm/src/cmd/recursive/filter.ts b/packages/pnpm/src/cmd/recursive/filter.ts index 7b69629ba5..7c937b5686 100644 --- a/packages/pnpm/src/cmd/recursive/filter.ts +++ b/packages/pnpm/src/cmd/recursive/filter.ts @@ -4,18 +4,18 @@ import { PackageNode } from 'pkgs-graph' import R = require('ramda') import { PackageSelector } from '../../parsePackageSelectors' -interface PackageGraph { - [id: string]: PackageNode, +interface PackageGraph { + [id: string]: PackageNode, } interface Graph { [nodeId: string]: string[], } -export function filterGraph ( - pkgGraph: PackageGraph, +export function filterGraph ( + pkgGraph: PackageGraph, packageSelectors: PackageSelector[], -): PackageGraph { +): PackageGraph { const cherryPickedPackages = [] as string[] const walkedDependencies = new Set() const walkedDependents = new Set() @@ -46,7 +46,7 @@ export function filterGraph ( return R.pick(Array.from(walked), pkgGraph) } -function pkgGraphToGraph (pkgGraph: PackageGraph): Graph { +function pkgGraphToGraph (pkgGraph: PackageGraph): Graph { const graph: Graph = {} Object.keys(pkgGraph).forEach((nodeId) => { graph[nodeId] = pkgGraph[nodeId].dependencies @@ -68,15 +68,15 @@ function reverseGraph (graph: Graph): Graph { return reversedGraph } -function matchPackages ( - graph: PackageGraph, +function matchPackages ( + graph: PackageGraph, pattern: string, ) { - return R.keys(graph).filter((id) => graph[id].manifest.name && minimatch(graph[id].manifest.name, pattern)) + return R.keys(graph).filter((id) => graph[id].package.manifest.name && minimatch(graph[id].package.manifest.name, pattern)) } -function matchPackagesByPath ( - graph: PackageGraph, +function matchPackagesByPath ( + graph: PackageGraph, pathStartsWith: string, ) { return R.keys(graph).filter((location) => isSubdir(pathStartsWith, location)) diff --git a/packages/pnpm/src/cmd/recursive/index.ts b/packages/pnpm/src/cmd/recursive/index.ts index 40205652a9..e2592e9cb1 100644 --- a/packages/pnpm/src/cmd/recursive/index.ts +++ b/packages/pnpm/src/cmd/recursive/index.ts @@ -22,14 +22,12 @@ import { rebuildPkgs, uninstall, } from 'supi' -import writePkg = require('write-pkg') import createStoreController from '../../createStoreController' import findWorkspacePackages, { arrayOfLocalPackagesToMap } from '../../findWorkspacePackages' import getCommandFullName from '../../getCommandFullName' import getPinnedVersion from '../../getPinnedVersion' import { scopeLogger } from '../../loggers' import parsePackageSelector, { PackageSelector } from '../../parsePackageSelectors' -import { readImporterManifestFromDir } from '../../readImporterManifest' import requireHooks from '../../requireHooks' import { PnpmOptions } from '../../types' import updateToLatestSpecsFromManifest, { createLatestSpecs } from '../../updateToLatestSpecsFromManifest' @@ -101,7 +99,7 @@ export default async ( } export async function recursive ( - allPkgs: Array<{path: string, manifest: DependencyManifest}>, + allPkgs: Array<{path: string, manifest: DependencyManifest, writeImporterManifest: (manifest: ImporterManifest) => Promise}>, input: string[], opts: PnpmOptions & { allowNew?: boolean, @@ -117,7 +115,7 @@ export async function recursive ( } const pkgGraphResult = createPkgGraph(allPkgs) - let pkgs: Array<{path: string, manifest: ImporterManifest}> + let pkgs: Array<{path: string, manifest: ImporterManifest, writeImporterManifest: (manifest: ImporterManifest) => Promise }> if (opts.packageSelectors && opts.packageSelectors.length) { pkgGraphResult.graph = filterGraph(pkgGraphResult.graph, opts.packageSelectors) pkgs = allPkgs.filter((pkg: {path: string}) => pkgGraphResult.graph[pkg.path]) @@ -128,6 +126,13 @@ export async function recursive ( if (pkgs.length === 0) { return false } + const manifestsByPath: { [path: string]: { manifest: ImporterManifest, writeImporterManifest: (manifest: ImporterManifest) => Promise } } = {} + for (const pkg of pkgs) { + manifestsByPath[pkg.path] = { + manifest: pkg.manifest, + writeImporterManifest: pkg.writeImporterManifest, + } + } scopeLogger.debug({ selected: pkgs.length, @@ -208,7 +213,7 @@ export async function recursive ( prefixes.map(async (prefix) => { importers.push({ buildIndex, - manifest: await readImporterManifestFromDir(prefix), + manifest: manifestsByPath[prefix].manifest, prefix, }) }) @@ -224,6 +229,7 @@ export async function recursive ( } if (cmdFullName !== 'rebuild') { + // For a workspace with shared lockfile if (opts.lockfileDirectory && ['install', 'uninstall', 'update'].includes(cmdFullName)) { let importers = await getImporters() const isFromWorkspace = isSubdir.bind(null, opts.lockfileDirectory) @@ -231,9 +237,11 @@ export async function recursive ( if (importers.length === 0) return true const hooks = opts.ignorePnpmfile ? {} : requireHooks(opts.lockfileDirectory, opts) const mutation = cmdFullName === 'uninstall' ? 'uninstallSome' : (input.length === 0 && !updateToLatest ? 'install' : 'installSome') - const mutatedImporters = await Promise.all(importers.map(async ({ buildIndex, prefix }) => { + const writeImporterManifests = [] as Array<(manifest: ImporterManifest) => Promise> + const mutatedImporters = await Promise.all(importers.map(async ({ buildIndex, prefix }, index) => { const localConfigs = await memReadLocalConfigs(prefix) - const manifest = await readImporterManifestFromDir(prefix) + const { manifest, writeImporterManifest } = manifestsByPath[prefix] + writeImporterManifests[index] = writeImporterManifest const shamefullyFlatten = typeof localConfigs.shamefullyFlatten === 'boolean' ? localConfigs.shamefullyFlatten : opts.shamefullyFlatten @@ -288,7 +296,7 @@ export async function recursive ( await Promise.all( mutatedPkgs .filter((mutatedPkg, index) => mutatedImporters[index].mutation !== 'install') - .map(({ manifest, prefix }) => writePkg(prefix, manifest)) + .map(({ manifest, prefix }, index) => writeImporterManifests[index](manifest)) ) return true } @@ -306,7 +314,7 @@ export async function recursive ( return } - const manifest = await readImporterManifestFromDir(prefix) + const { manifest, writeImporterManifest } = manifestsByPath[prefix] let currentInput = [...input] if (updateToLatest) { if (!currentInput || !currentInput.length) { @@ -330,7 +338,7 @@ export async function recursive ( } const localConfigs = await memReadLocalConfigs(prefix) - const newPkg = await action( + const newManifest = await action( manifest, { ...installOpts, @@ -347,7 +355,7 @@ export async function recursive ( }, ) if (action !== install) { - await writePkg(prefix, newPkg) + await writeImporterManifest(newManifest) } result.passes++ } catch (err) { @@ -404,7 +412,7 @@ export async function recursive ( [ { buildIndex: 0, - manifest: await readImporterManifestFromDir(prefix), + manifest: manifestsByPath[prefix].manifest, prefix, }, ], @@ -473,7 +481,7 @@ async function unlinkPkgs (dependencyNames: string[], manifest: ImporterManifest ) } -function sortPackages (pkgGraph: {[nodeId: string]: PackageNode}): string[][] { +function sortPackages (pkgGraph: {[nodeId: string]: PackageNode}): string[][] { const keys = Object.keys(pkgGraph) const setOfKeys = new Set(keys) const graph = new Map( diff --git a/packages/pnpm/src/cmd/recursive/run.ts b/packages/pnpm/src/cmd/recursive/run.ts index a40207721a..59884df19e 100644 --- a/packages/pnpm/src/cmd/recursive/run.ts +++ b/packages/pnpm/src/cmd/recursive/run.ts @@ -6,9 +6,9 @@ import pLimit = require('p-limit') import { PackageNode } from 'pkgs-graph' import RecursiveSummary from './recursiveSummary' -export default async ( +export default async ( packageChunks: string[][], - graph: {[id: string]: PackageNode}, + graph: {[id: string]: PackageNode}, args: string[], cmd: string, opts: { @@ -35,8 +35,8 @@ export default async ( for (const chunk of packageChunks) { await Promise.all(chunk.map((prefix: string) => limitRun(async () => { - const pkg = graph[prefix] as {manifest: PackageJson, path: string} - if (!pkg.manifest.scripts || !pkg.manifest.scripts[scriptName]) { + const pkg = graph[prefix] as {package: {manifest: PackageJson, path: string}} + if (!pkg.package.manifest.scripts || !pkg.package.manifest.scripts[scriptName]) { return } hasCommand++ @@ -49,12 +49,12 @@ export default async ( stdio, unsafePerm: true, // when running scripts explicitly, assume that they're trusted. } - if (pkg.manifest.scripts[`pre${scriptName}`]) { - await runLifecycleHooks(`pre${scriptName}`, pkg.manifest, lifecycleOpts) + if (pkg.package.manifest.scripts[`pre${scriptName}`]) { + await runLifecycleHooks(`pre${scriptName}`, pkg.package.manifest, lifecycleOpts) } - await runLifecycleHooks(scriptName, pkg.manifest, lifecycleOpts) - if (pkg.manifest.scripts[`post${scriptName}`]) { - await runLifecycleHooks(`post${scriptName}`, pkg.manifest, lifecycleOpts) + await runLifecycleHooks(scriptName, pkg.package.manifest, lifecycleOpts) + if (pkg.package.manifest.scripts[`post${scriptName}`]) { + await runLifecycleHooks(`post${scriptName}`, pkg.package.manifest, lifecycleOpts) } result.passes++ } catch (err) { diff --git a/packages/pnpm/src/cmd/uninstall.ts b/packages/pnpm/src/cmd/uninstall.ts index 542c20b373..c9aa1310db 100644 --- a/packages/pnpm/src/cmd/uninstall.ts +++ b/packages/pnpm/src/cmd/uninstall.ts @@ -1,11 +1,10 @@ +import readImporterManifest from '@pnpm/read-importer-manifest' import { mutateModules, uninstall, } from 'supi' -import writePkg = require('write-pkg') import createStoreController from '../createStoreController' import findWorkspacePackages, { arrayOfLocalPackagesToMap } from '../findWorkspacePackages' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' export default async function uninstallCmd ( @@ -18,24 +17,26 @@ export default async function uninstallCmd ( storeController: store.ctrl, }) if (opts.lockfileDirectory === opts.prefix) { - const manifest = await uninstall(await readImporterManifestFromDir(opts.prefix), input, uninstallOpts) - await writePkg(opts.prefix, manifest) + const { manifest, writeImporterManifest } = await readImporterManifest(opts.prefix) + const newManifest = await uninstall(manifest, input, uninstallOpts) + await writeImporterManifest(newManifest) return } uninstallOpts['localPackages'] = opts.linkWorkspacePackages && opts.workspacePrefix ? arrayOfLocalPackagesToMap(await findWorkspacePackages(opts.workspacePrefix)) : undefined - const [{ manifest }] = await mutateModules( + const currentManifest = await readImporterManifest(opts.prefix) + const [mutationResult] = await mutateModules( [ { bin: opts.bin, dependencyNames: input, - manifest: await readImporterManifestFromDir(opts.prefix), + manifest: currentManifest.manifest, mutation: 'uninstallSome', prefix: opts.prefix, }, ], uninstallOpts, ) - await writePkg(opts.prefix, manifest) + await currentManifest.writeImporterManifest(mutationResult.manifest) } diff --git a/packages/pnpm/src/cmd/unlink.ts b/packages/pnpm/src/cmd/unlink.ts index 298d478805..132fbcdc52 100644 --- a/packages/pnpm/src/cmd/unlink.ts +++ b/packages/pnpm/src/cmd/unlink.ts @@ -1,6 +1,6 @@ +import { readImporterManifestOnly } from '@pnpm/read-importer-manifest' import { mutateModules } from 'supi' import createStoreController from '../createStoreController' -import { readImporterManifestFromDir } from '../readImporterManifest' import { PnpmOptions } from '../types' export default async function (input: string[], opts: PnpmOptions) { @@ -14,7 +14,7 @@ export default async function (input: string[], opts: PnpmOptions) { return mutateModules([ { dependencyNames: input, - manifest: await readImporterManifestFromDir(opts.prefix), + manifest: await readImporterManifestOnly(opts.prefix), mutation: 'unlinkSome', prefix: opts.prefix, }, @@ -22,7 +22,7 @@ export default async function (input: string[], opts: PnpmOptions) { } return mutateModules([ { - manifest: await readImporterManifestFromDir(opts.prefix), + manifest: await readImporterManifestOnly(opts.prefix), mutation: 'unlink', prefix: opts.prefix, }, diff --git a/packages/pnpm/src/findWorkspacePackages.ts b/packages/pnpm/src/findWorkspacePackages.ts index f7c350844a..299a71d7c9 100644 --- a/packages/pnpm/src/findWorkspacePackages.ts +++ b/packages/pnpm/src/findWorkspacePackages.ts @@ -1,10 +1,10 @@ import { WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants' -import { DependencyManifest } from '@pnpm/types' +import { DependencyManifest, ImporterManifest } from '@pnpm/types' import findPackages from 'find-packages' import path = require('path') import readYamlFile from 'read-yaml-file' -export default async (workspaceRoot: string): Promise> => { +export default async (workspaceRoot: string): Promise Promise}>> => { const packagesManifest = await requirePackagesManifest(workspaceRoot) const pkgs = await findPackages(workspaceRoot, { ignore: [ diff --git a/packages/pnpm/test/install/misc.ts b/packages/pnpm/test/install/misc.ts index acfa4954a3..c256284c21 100644 --- a/packages/pnpm/test/install/misc.ts +++ b/packages/pnpm/test/install/misc.ts @@ -1,12 +1,13 @@ import { WANTED_LOCKFILE } from '@pnpm/constants' import { Lockfile } from '@pnpm/lockfile-types' import prepare, { prepareEmpty } from '@pnpm/prepare' +import readImporterManifest from '@pnpm/read-importer-manifest' import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json' +import writeImporterManifest from '@pnpm/write-importer-manifest' import crossSpawn = require('cross-spawn') import delay = require('delay') import dirIsCaseSensitive from 'dir-is-case-sensitive' import fs = require('fs') -import isWindows = require('is-windows') import loadJsonFile = require('load-json-file') import path = require('path') import exists = require('path-exists') @@ -122,6 +123,20 @@ test('install --save-exact', async (t: tape.Test) => { t.deepEqual(pkg.devDependencies, { 'is-positive': '3.1.0' }) }) +test('install to a project that uses package.yaml', async (t: tape.Test) => { + const project = prepareEmpty(t) + + await writeImporterManifest(path.resolve('package.yaml'), { name: 'foo', version: '1.0.0' }) + + await execPnpm('install', 'is-positive@3.1.0', '--save-exact', '--save-dev') + + await project.has('is-positive') + + const { manifest } = await readImporterManifest(process.cwd()) + + t.deepEqual(manifest && manifest.devDependencies, { 'is-positive': '3.1.0' }) +}) + test('install save new dep with the specified spec', async (t: tape.Test) => { const project = prepare(t) diff --git a/packages/pnpm/typings/local.d.ts b/packages/pnpm/typings/local.d.ts index 869d9527d4..997546c6b1 100644 --- a/packages/pnpm/typings/local.d.ts +++ b/packages/pnpm/typings/local.d.ts @@ -173,11 +173,6 @@ declare module 'pnpm-registry-mock' { export = anything; } -declare module 'read-package-json' { - const anything: any; - export = anything; -} - declare module 'ssri' { const anything: any; export = anything; diff --git a/packages/read-importer-manifest/LICENSE b/packages/read-importer-manifest/LICENSE new file mode 100644 index 0000000000..e233d0b91c --- /dev/null +++ b/packages/read-importer-manifest/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-2019 Zoltan Kochan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/read-importer-manifest/README.md b/packages/read-importer-manifest/README.md new file mode 100644 index 0000000000..278d87d921 --- /dev/null +++ b/packages/read-importer-manifest/README.md @@ -0,0 +1,25 @@ +# @pnpm/read-importer-manifest + +> Read an importer manifest (called package.json in most cases) + + +[![npm version](https://img.shields.io/npm/v/@pnpm/read-importer-manifest.svg)](https://www.npmjs.com/package/@pnpm/read-importer-manifest) + + +## Installation + +```sh + add @pnpm/read-importer-manifest +``` + +## Usage + +```ts +import readImporterManifest from '@pnpm/read-importer-manifest' + +const { manifest, fileName } = await readImporterManifest(process.cwd()) +``` + +## License + +[MIT](./LICENSE) © [Zoltan Kochan](https://www.kochan.io/) diff --git a/packages/read-importer-manifest/package.json b/packages/read-importer-manifest/package.json new file mode 100644 index 0000000000..ff8b4a8594 --- /dev/null +++ b/packages/read-importer-manifest/package.json @@ -0,0 +1,71 @@ +{ + "name": "@pnpm/read-importer-manifest", + "version": "0.0.0", + "description": "Read an importer manifest (called package.json in most cases)", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "engines": { + "node": ">=8.15" + }, + "files": [ + "lib" + ], + "scripts": { + "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts", + "test": "npm run lint && npm run tsc && ts-node test --type-check", + "prepublishOnly": "tsc", + "tsc": "tsc", + "md": "mos" + }, + "repository": "https://github.com/pnpm/pnpm/blob/master/packages/read-importer-manifest", + "keywords": [ + "pnpm", + "outdated" + ], + "author": { + "name": "Zoltan Kochan", + "email": "z@kochan.io", + "url": "https://www.kochan.io/", + "twitter": "ZoltanKochan" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/pnpm/pnpm/issues" + }, + "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/read-importer-manifest#readme", + "dependencies": { + "@pnpm/types": "^3.0.0", + "@pnpm/write-importer-manifest": "0.0.0", + "@types/detect-indent": "5.0.0", + "detect-indent": "5.0.0", + "graceful-fs": "4.1.15", + "is-windows": "1.0.2", + "json5": "2.1.0", + "read-yaml-file": "1.1.0", + "strip-bom": "4.0.0" + }, + "devDependencies": { + "@pnpm/read-importer-manifest": "link:", + "@pnpm/tslint-config": "0.0.0", + "@types/graceful-fs": "4.1.3", + "@types/is-windows": "0.2.0", + "@types/json5": "0.0.30", + "@types/node": "^10.3.2", + "@types/tape": "^4.2.31", + "mos": "^2.0.0-alpha.3", + "mos-plugin-readme": "^1.0.4", + "tape": "^4.8.0", + "tempy": "0.3.0", + "ts-node": "8.1.0", + "tslint": "^5.8.0", + "typescript": "3.4.5" + }, + "mos": { + "plugins": [ + "readme" + ], + "installation": { + "useShortAlias": true + } + } +} diff --git a/packages/read-importer-manifest/src/index.ts b/packages/read-importer-manifest/src/index.ts new file mode 100644 index 0000000000..bd03637e0b --- /dev/null +++ b/packages/read-importer-manifest/src/index.ts @@ -0,0 +1,158 @@ +import { ImporterManifest } from '@pnpm/types' +import writeImporterManifest from '@pnpm/write-importer-manifest' +import detectIndent = require('detect-indent') +import fs = require('fs') +import { Stats } from 'fs' +import isWindows = require('is-windows') +import path = require('path') +import readYamlFile from 'read-yaml-file' +import { promisify } from 'util' +import { + readJson5File, + readJsonFile, +} from './readFile' + +const stat = promisify(fs.stat) + +export default async function readImporterManifest (importerDir: string): Promise<{ + manifest: ImporterManifest + writeImporterManifest: (manifest: ImporterManifest) => Promise +}> { + const result = await tryReadImporterManifest(importerDir) + if (result.manifest !== null) { + return result as { + manifest: ImporterManifest + writeImporterManifest: (manifest: ImporterManifest) => Promise + } + } + const err = new Error(`No package.json (or package.yaml, or package.json5) was found in "${importerDir}".`) + err['code'] = 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND' + throw err +} + +export async function readImporterManifestOnly (importerDir: string): Promise { + const { manifest } = await readImporterManifest(importerDir) + return manifest +} + +export async function tryReadImporterManifest (importerDir: string): Promise<{ + manifest: ImporterManifest | null + writeImporterManifest: (manifest: ImporterManifest) => Promise +}> { + try { + const manifestPath = path.join(importerDir, 'package.json') + const { data, text } = await readJsonFile(manifestPath) + const { indent } = detectIndent(text) + return { + manifest: data, + writeImporterManifest: createManifestWriter({ + indent, + initialManifest: data, + manifestPath, + }), + } + } catch (err) { + if (err.code !== 'ENOENT') throw err + } + try { + const manifestPath = path.join(importerDir, 'package.json5') + const { data, text } = await readJson5File(manifestPath) + const { indent } = detectIndent(text) + return { + manifest: data, + writeImporterManifest: createManifestWriter({ + indent, + initialManifest: data, + manifestPath, + }), + } + } catch (err) { + if (err.code !== 'ENOENT') throw err + } + try { + const manifestPath = path.join(importerDir, 'package.yaml') + const manifest = await readPackageYaml(manifestPath) + return { + manifest, + writeImporterManifest: createManifestWriter({ initialManifest: manifest, manifestPath }), + } + } catch (err) { + if (err.code !== 'ENOENT') throw err + } + if (isWindows()) { + // ENOTDIR isn't used on Windows, but pnpm expects it. + let s: Stats | undefined + try { + s = await stat(importerDir) + } catch (err) { + // Ignore + } + if (s && !s.isDirectory()) { + const err = new Error(`"${importerDir}" is not a directory`) + err['code'] = 'ENOTDIR' // tslint:disable-line + throw err + } + } + const filePath = path.join(importerDir, 'package.json') + return { + manifest: null, + writeImporterManifest: writeImporterManifest.bind(null, filePath), + } +} + +export async function readExactImporterManifest (manifestPath: string) { + const base = path.basename(manifestPath).toLowerCase() + switch (base) { + case 'package.json': { + const { data, text } = await readJsonFile(manifestPath) + const { indent } = detectIndent(text) + return { + manifest: data, + writeImporterManifest: createManifestWriter({ + indent, + initialManifest: data, + manifestPath, + }), + } + } + case 'package.json5': { + const { data, text } = await readJson5File(manifestPath) + const { indent } = detectIndent(text) + return { + manifest: data, + writeImporterManifest: createManifestWriter({ + indent, + initialManifest: data, + manifestPath, + }), + } + } + case 'package.yaml': { + const manifest = await readPackageYaml(manifestPath) + return { + manifest, + writeImporterManifest: createManifestWriter({ initialManifest: manifest, manifestPath }), + } + } + } + throw new Error(`Not supported manifest name "${base}"`) +} + +function readPackageYaml (filePath: string) { + return readYamlFile(filePath) +} + +function createManifestWriter ( + opts: { + initialManifest: ImporterManifest, + indent?: string | number | null | undefined, + manifestPath: string, + }, +): ((manifest: ImporterManifest) => Promise) { + const stringifiedInitialManifest = JSON.stringify(opts.initialManifest) + return async (updatedManifest: ImporterManifest) => { + if (stringifiedInitialManifest !== JSON.stringify(updatedManifest)) { + return writeImporterManifest(opts.manifestPath, updatedManifest, { indent: opts.indent }) + } + } +} diff --git a/packages/read-importer-manifest/src/readFile.ts b/packages/read-importer-manifest/src/readFile.ts new file mode 100644 index 0000000000..46f759d8c4 --- /dev/null +++ b/packages/read-importer-manifest/src/readFile.ts @@ -0,0 +1,26 @@ +import { ImporterManifest } from '@pnpm/types' +import fs = require('graceful-fs') +import JSON5 = require('json5') +import stripBom = require('strip-bom') +import { promisify } from 'util' +const readFile = promisify(fs.readFile) + +export async function readJson5File (filePath: string) { + const text = await readFileWithoutBom(filePath) + return { + data: JSON5.parse(text) as ImporterManifest, + text, + } +} + +export async function readJsonFile (filePath: string) { + const text = await readFileWithoutBom(filePath) + return { + data: JSON.parse(text) as ImporterManifest, + text, + } +} + +async function readFileWithoutBom (path: string) { + return stripBom(await readFile(path, 'utf8')) +} diff --git a/packages/read-importer-manifest/test/fixtures/package-json/package.json b/packages/read-importer-manifest/test/fixtures/package-json/package.json new file mode 100644 index 0000000000..da86787ad3 --- /dev/null +++ b/packages/read-importer-manifest/test/fixtures/package-json/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "version": "1.0.0" +} diff --git a/packages/read-importer-manifest/test/fixtures/package-json5/package.json5 b/packages/read-importer-manifest/test/fixtures/package-json5/package.json5 new file mode 100644 index 0000000000..54f7b07641 --- /dev/null +++ b/packages/read-importer-manifest/test/fixtures/package-json5/package.json5 @@ -0,0 +1,4 @@ +{ + name: 'foo', + version: '1.0.0', +} diff --git a/packages/read-importer-manifest/test/fixtures/package-yaml/package.yaml b/packages/read-importer-manifest/test/fixtures/package-yaml/package.yaml new file mode 100644 index 0000000000..607ebe8ebe --- /dev/null +++ b/packages/read-importer-manifest/test/fixtures/package-yaml/package.yaml @@ -0,0 +1,2 @@ +name: foo +version: 1.0.0 diff --git a/packages/read-importer-manifest/test/index.ts b/packages/read-importer-manifest/test/index.ts new file mode 100644 index 0000000000..39be449764 --- /dev/null +++ b/packages/read-importer-manifest/test/index.ts @@ -0,0 +1,110 @@ +import readImporterManifest, { tryReadImporterManifest } from '@pnpm/read-importer-manifest' +import fs = require('graceful-fs') +import path = require('path') +import test = require('tape') +import tempy = require('tempy') +import { promisify } from 'util' + +const writeFile = promisify(fs.writeFile) +const readFile = promisify(fs.readFile) +const stat = promisify(fs.stat) + +const fixtures = path.join(__dirname, 'fixtures') + +test('readImporterManifest()', async (t) => { + t.deepEqual( + (await tryReadImporterManifest(path.join(fixtures, 'package-json'))).manifest, + { name: 'foo', version: '1.0.0' }, + ) + + t.deepEqual( + (await tryReadImporterManifest(path.join(fixtures, 'package-json5'))).manifest, + { name: 'foo', version: '1.0.0' }, + ) + + t.deepEqual( + (await tryReadImporterManifest(path.join(fixtures, 'package-yaml'))).manifest, + { name: 'foo', version: '1.0.0' }, + ) + + t.deepEqual( + (await tryReadImporterManifest(fixtures)).manifest, + null, + ) + + t.end() +}) + +test('preserve tab indentation in json file', async (t) => { + process.chdir(tempy.directory()) + + await writeFile('package.json', '{\n\t"name": "foo"\n}\n', 'utf8') + + const { manifest, writeImporterManifest } = await readImporterManifest(process.cwd()) + + await writeImporterManifest({ ...manifest, dependencies: { bar: '1.0.0' } }) + + const rawManifest = await readFile('package.json', 'utf8') + t.equal(rawManifest, '{\n\t"name": "foo",\n\t"dependencies": {\n\t\t"bar": "1.0.0"\n\t}\n}\n') + t.end() +}) + +test('preserve space indentation in json file', async (t) => { + process.chdir(tempy.directory()) + + await writeFile('package.json', '{\n "name": "foo"\n}\n', 'utf8') + + const { manifest, writeImporterManifest } = await readImporterManifest(process.cwd()) + + await writeImporterManifest({ ...manifest, dependencies: { bar: '1.0.0' } }) + + const rawManifest = await readFile('package.json', 'utf8') + t.equal(rawManifest, '{\n "name": "foo",\n "dependencies": {\n "bar": "1.0.0"\n }\n}\n') + t.end() +}) + +test('preserve tab indentation in json5 file', async (t) => { + process.chdir(tempy.directory()) + + await writeFile('package.json5', "{\n\tname: 'foo',\n}\n", 'utf8') + + const { manifest, writeImporterManifest } = await readImporterManifest(process.cwd()) + + await writeImporterManifest({ ...manifest, dependencies: { bar: '1.0.0' } }) + + const rawManifest = await readFile('package.json5', 'utf8') + t.equal(rawManifest, "{\n\tname: 'foo',\n\tdependencies: {\n\t\tbar: '1.0.0',\n\t},\n}\n") + t.end() +}) + +test('preserve space indentation in json5 file', async (t) => { + process.chdir(tempy.directory()) + + await writeFile('package.json5', "{\n name: 'foo'\n}\n", 'utf8') + + const { manifest, writeImporterManifest } = await readImporterManifest(process.cwd()) + + await writeImporterManifest({ ...manifest, dependencies: { bar: '1.0.0' } }) + + const rawManifest = await readFile('package.json5', 'utf8') + t.equal(rawManifest, "{\n name: 'foo',\n dependencies: {\n bar: '1.0.0',\n },\n}\n") + t.end() +}) + +test('do not save manifest if it had no changes', async (t) => { + process.chdir(tempy.directory()) + + await writeFile('package.json5', JSON.stringify({ dependencies: { foo: '*', bar: '*' } }), 'utf8') + + const { manifest, writeImporterManifest } = await readImporterManifest(process.cwd()) + + const stat1 = await stat('package.json5') + + await writeImporterManifest(manifest) + + const stat2 = await stat('package.json5') + + t.deepEqual(stat1.ino, stat2.ino, 'manifest was not resaved') + + t.end() +}) diff --git a/packages/read-importer-manifest/tsconfig.json b/packages/read-importer-manifest/tsconfig.json new file mode 100644 index 0000000000..bcef9368e9 --- /dev/null +++ b/packages/read-importer-manifest/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../utils/tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "src/**/*.ts", + "typings/**/*.d.ts", + "test/typings/**/*.d.ts" + ] +} diff --git a/packages/read-importer-manifest/tslint.json b/packages/read-importer-manifest/tslint.json new file mode 100644 index 0000000000..568acfabec --- /dev/null +++ b/packages/read-importer-manifest/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@pnpm/tslint-config" +} diff --git a/packages/supi/package.json b/packages/supi/package.json index 8601be20ad..433b03eab5 100644 --- a/packages/supi/package.json +++ b/packages/supi/package.json @@ -35,6 +35,7 @@ "@pnpm/package-requester": "7.0.6", "@pnpm/pkgid-to-filename": "2.0.0", "@pnpm/prune-lockfile": "1.0.4", + "@pnpm/read-importer-manifest": "0.0.0", "@pnpm/read-importers-context": "1.0.1", "@pnpm/read-modules-dir": "2.0.0", "@pnpm/read-package-json": "2.0.1", diff --git a/packages/supi/src/link/index.ts b/packages/supi/src/link/index.ts index 042d6ff0ef..e613296734 100644 --- a/packages/supi/src/link/index.ts +++ b/packages/supi/src/link/index.ts @@ -11,6 +11,7 @@ import { import logger, { streamParser } from '@pnpm/logger' import { prune } from '@pnpm/modules-cleaner' import { pruneSharedLockfile } from '@pnpm/prune-lockfile' +import readImporterManifest from '@pnpm/read-importer-manifest' import { symlinkDirectRootDependency } from '@pnpm/symlink-dependency' import { DEPENDENCIES_FIELDS, @@ -21,7 +22,6 @@ import { import { getSaveType, } from '@pnpm/utils' -import loadJsonFile = require('load-json-file') import normalize = require('normalize-path') import path = require('path') import pathAbsolute = require('path-absolute') @@ -66,18 +66,18 @@ export default async function link ( linkFromPath = linkFrom.path linkFromAlias = linkFrom.alias } - const linkedPkg = await loadJsonFile(path.join(linkFromPath, 'package.json')) + const { manifest } = await readImporterManifest(linkFromPath) as { manifest: DependencyManifest } specsToUpsert.push({ - name: linkedPkg.name, - pref: getPref(linkedPkg.name, linkedPkg.name, linkedPkg.version, { + name: manifest.name, + pref: getPref(manifest.name, manifest.name, manifest.version, { pinnedVersion: opts.pinnedVersion, }), - saveType: (saveType || ctx.manifest && guessDependencyType(linkedPkg.name, ctx.manifest)) as DependenciesField, + saveType: (saveType || ctx.manifest && guessDependencyType(manifest.name, ctx.manifest)) as DependenciesField, }) const packagePath = normalize(path.relative(opts.prefix, linkFromPath)) const addLinkOpts = { - linkedPkgName: linkFromAlias || linkedPkg.name, + linkedPkgName: linkFromAlias || manifest.name, manifest: ctx.manifest, packagePath, } @@ -85,8 +85,8 @@ export default async function link ( addLinkToLockfile(ctx.wantedLockfile.importers[importerId], addLinkOpts) linkedPkgs.push({ - alias: linkFromAlias || linkedPkg.name, - manifest: linkedPkg, + alias: linkFromAlias || manifest.name, + manifest, path: linkFromPath, }) } diff --git a/packages/write-importer-manifest/LICENSE b/packages/write-importer-manifest/LICENSE new file mode 100644 index 0000000000..e233d0b91c --- /dev/null +++ b/packages/write-importer-manifest/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-2019 Zoltan Kochan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/write-importer-manifest/README.md b/packages/write-importer-manifest/README.md new file mode 100644 index 0000000000..dd73a07e2c --- /dev/null +++ b/packages/write-importer-manifest/README.md @@ -0,0 +1,26 @@ +# @pnpm/write-importer-manifest + +> Write an importer manifest (called package.json in most cases) + + +[![npm version](https://img.shields.io/npm/v/@pnpm/write-importer-manifest.svg)](https://www.npmjs.com/package/@pnpm/write-importer-manifest) + + +## Installation + +```sh + add @pnpm/write-importer-manifest +``` + +## Usage + +```ts +import writeImporterManifest from '@pnpm/write-importer-manifest' +import path = require('path') + +(async () => await writeImporterManifest(path.resolve('package.yaml'), { name: 'foo', version: '1.0.0' }))() +``` + +## License + +[MIT](./LICENSE) © [Zoltan Kochan](https://www.kochan.io/) diff --git a/packages/write-importer-manifest/package.json b/packages/write-importer-manifest/package.json new file mode 100644 index 0000000000..8ed365081b --- /dev/null +++ b/packages/write-importer-manifest/package.json @@ -0,0 +1,64 @@ +{ + "name": "@pnpm/write-importer-manifest", + "version": "0.0.0", + "description": "Write an importer manifest (called package.json in most cases)", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "engines": { + "node": ">=8.15" + }, + "files": [ + "lib" + ], + "scripts": { + "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts", + "test": "npm run lint && npm run tsc && ts-node test --type-check", + "prepublishOnly": "tsc", + "tsc": "tsc", + "md": "mos" + }, + "repository": "https://github.com/pnpm/pnpm/blob/master/packages/write-importer-manifest", + "keywords": [ + "pnpm", + "outdated" + ], + "author": { + "name": "Zoltan Kochan", + "email": "z@kochan.io", + "url": "https://www.kochan.io/", + "twitter": "ZoltanKochan" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/pnpm/pnpm/issues" + }, + "homepage": "https://github.com/pnpm/pnpm/blob/master/packages/write-importer-manifest#readme", + "dependencies": { + "@pnpm/types": "^3.0.0", + "sort-keys": "2.0.0", + "write-json-file": "3.2.0", + "write-json5-file": "2.0.0", + "write-yaml-file": "2.0.0" + }, + "devDependencies": { + "@pnpm/tslint-config": "0.0.0", + "@pnpm/write-importer-manifest": "link:", + "@types/node": "^10.3.2", + "@types/tape": "^4.2.31", + "mos": "^2.0.0-alpha.3", + "mos-plugin-readme": "^1.0.4", + "tape": "^4.8.0", + "tempy": "0.3.0", + "ts-node": "8.1.0", + "tslint": "^5.8.0", + "typescript": "3.4.5" + }, + "mos": { + "plugins": [ + "readme" + ], + "installation": { + "useShortAlias": true + } + } +} diff --git a/packages/write-importer-manifest/src/index.ts b/packages/write-importer-manifest/src/index.ts new file mode 100644 index 0000000000..008861cb51 --- /dev/null +++ b/packages/write-importer-manifest/src/index.ts @@ -0,0 +1,44 @@ +import { ImporterManifest } from '@pnpm/types' +import sortKeys = require('sort-keys') +import writeJsonFile = require('write-json-file') +import writeJson5File = require('write-json5-file') +import writeYamlFile = require('write-yaml-file') + +// TODO: normalize before save + preserve indent +export default function writeImporterManifest ( + filePath: string, + manifest: ImporterManifest, + opts?: { indent?: string | number | null }, +): Promise { + manifest = normalize(manifest) + switch (filePath.substr(filePath.lastIndexOf('.') + 1).toLowerCase()) { + case 'json5': + return writeJson5File(filePath, manifest, opts) + case 'yaml': + return writeYamlFile(filePath, manifest) + case 'json': + default: + return writeJsonFile(filePath, manifest, opts) + } +} + +const dependencyKeys = new Set([ + 'dependencies', + 'devDependencies', + 'optionalDependencies', + 'peerDependencies', +]) + +function normalize (manifest: ImporterManifest) { + const result = {} + + for (const key of Object.keys(manifest)) { + if (!dependencyKeys.has(key)) { + result[key] = manifest[key] + } else if (Object.keys(manifest[key]).length !== 0) { + result[key] = sortKeys(manifest[key]) + } + } + + return result +} diff --git a/packages/write-importer-manifest/test/index.ts b/packages/write-importer-manifest/test/index.ts new file mode 100644 index 0000000000..e7b422e3eb --- /dev/null +++ b/packages/write-importer-manifest/test/index.ts @@ -0,0 +1,23 @@ +import writeImporterManifest from '@pnpm/write-importer-manifest' +import fs = require('fs') +import path = require('path') +import test = require('tape') +import tempy = require('tempy') +import { promisify } from 'util' + +const readFile = promisify(fs.readFile) + +test('writeImporterManifest()', async (t) => { + const dir = tempy.directory() + + await writeImporterManifest(path.join(dir, 'package.json'), { name: 'foo', version: '1.0.0' }) + t.equal(await readFile(path.join(dir, 'package.json'), 'utf8'), '{\n\t"name": "foo",\n\t"version": "1.0.0"\n}\n') + + await writeImporterManifest(path.join(dir, 'package.json5'), { name: 'foo', version: '1.0.0' }) + t.equal(await readFile(path.join(dir, 'package.json5'), 'utf8'), "{\n\tname: 'foo',\n\tversion: '1.0.0',\n}\n") + + await writeImporterManifest(path.join(dir, 'package.yaml'), { name: 'foo', version: '1.0.0' }) + t.equal(await readFile(path.join(dir, 'package.yaml'), 'utf8'), 'name: foo\nversion: 1.0.0\n') + + t.end() +}) diff --git a/packages/write-importer-manifest/tsconfig.json b/packages/write-importer-manifest/tsconfig.json new file mode 100644 index 0000000000..bcef9368e9 --- /dev/null +++ b/packages/write-importer-manifest/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../utils/tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "src/**/*.ts", + "typings/**/*.d.ts", + "test/typings/**/*.d.ts" + ] +} diff --git a/packages/write-importer-manifest/tslint.json b/packages/write-importer-manifest/tslint.json new file mode 100644 index 0000000000..568acfabec --- /dev/null +++ b/packages/write-importer-manifest/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@pnpm/tslint-config" +} diff --git a/packages/write-importer-manifest/typings/index.d.ts b/packages/write-importer-manifest/typings/index.d.ts new file mode 100644 index 0000000000..e406ba40c2 --- /dev/null +++ b/packages/write-importer-manifest/typings/index.d.ts @@ -0,0 +1,4 @@ +declare module 'sort-keys' { + const anything: any; + export = anything; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6712bf81ab..5fb2ce1fbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -461,6 +461,36 @@ importers: typescript: 3.4.5 write-yaml-file: 2.0.0 yaml-tag: 1.1.0 + packages/find-packages: + dependencies: + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' + '@types/node': 10.14.6 + fast-glob: 2.2.6 + p-filter: 2.1.0 + devDependencies: + '@pnpm/tslint-config': 'link:../../utils/tslint-config' + '@types/tape': 4.2.33 + find-packages: 'link:' + mos: 2.0.0-alpha.3 + mos-plugin-readme: 1.0.4 + tape: 4.10.1 + ts-node: 8.1.0_typescript@3.4.5 + tslint: 5.16.0_typescript@3.4.5 + typescript: 3.4.5 + specifiers: + '@pnpm/read-importer-manifest': 0.0.0 + '@pnpm/tslint-config': 0.0.0 + '@types/node': ^10.12.18 + '@types/tape': ^4.2.29 + fast-glob: ^2.0.4 + find-packages: 'link:' + mos: ^2.0.0-alpha.3 + mos-plugin-readme: ^1.0.4 + p-filter: ^2.0.0 + tape: ^4.6.3 + ts-node: ^8.0.1 + tslint: ^5.0.0 + typescript: ^3.0.0 packages/git-fetcher: dependencies: '@types/node': 11.13.8 @@ -695,6 +725,7 @@ importers: typescript: 3.4.5 packages/list: dependencies: + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' '@pnpm/read-package-json': 'link:../read-package-json' '@pnpm/types': 'link:../types' '@types/archy': 0.0.31 @@ -722,6 +753,7 @@ importers: specifiers: '@pnpm/list': 'link:' '@pnpm/logger': 2.1.0 + '@pnpm/read-importer-manifest': 0.0.0 '@pnpm/read-package-json': 2.0.1 '@pnpm/tslint-config': 0.0.0 '@pnpm/types': 3.2.0 @@ -745,7 +777,7 @@ importers: typescript: 3.4.5 packages/local-resolver: dependencies: - '@pnpm/read-package-json': 'link:../read-package-json' + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' '@pnpm/resolver-base': 'link:../resolver-base' '@pnpm/types': 'link:../types' '@types/graceful-fs': 4.1.3 @@ -765,7 +797,7 @@ importers: typescript: 3.4.5 specifiers: '@pnpm/local-resolver': 'link:' - '@pnpm/read-package-json': 2.0.1 + '@pnpm/read-importer-manifest': 0.0.0 '@pnpm/resolver-base': 3.1.2 '@pnpm/tslint-config': 0.0.0 '@pnpm/types': 3.2.0 @@ -1024,7 +1056,7 @@ importers: '@pnpm/logger': 2.1.0 '@pnpm/npm-resolver': 'link:' '@pnpm/tslint-config': 'link:../../utils/tslint-config' - '@types/nock': 10.0.0 + '@types/nock': 10.0.1 '@types/normalize-path': 3.0.0 '@types/path-exists': 3.0.0 '@types/ssri': 6.0.1 @@ -1079,7 +1111,7 @@ importers: '@pnpm/constants': 'link:../constants' '@pnpm/lockfile-file': 'link:../lockfile-file' '@pnpm/npm-resolver': 'link:../npm-resolver' - '@pnpm/read-package-json': 'link:../read-package-json' + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' '@pnpm/store-path': 1.0.4 '@pnpm/types': 'link:../types' '@pnpm/utils': 'link:../utils' @@ -1104,7 +1136,7 @@ importers: '@pnpm/logger': 2.1.0 '@pnpm/npm-resolver': 3.0.7 '@pnpm/outdated': 'link:' - '@pnpm/read-package-json': 2.0.1 + '@pnpm/read-importer-manifest': 0.0.0 '@pnpm/store-path': 1.0.4 '@pnpm/tslint-config': 0.0.0 '@pnpm/types': 3.2.0 @@ -1318,6 +1350,38 @@ importers: tslint: 5.16.0 typescript: 3.4.5 write-json-file: 3.2.0 + packages/pkgs-graph: + dependencies: + '@types/node': 11.13.8 + '@types/ramda': 0.26.8 + '@types/semver': 5.5.0 + '@zkochan/npm-package-arg': 1.0.2 + ramda: 0.26.1 + semver: 6.0.0 + devDependencies: + '@pnpm/tslint-config': 'link:../../utils/tslint-config' + '@types/tape': 4.2.33 + better-path-resolve: 1.0.0 + pkgs-graph: 'link:' + tape: 4.10.1 + ts-node: 8.1.0_typescript@3.4.5 + tslint: 5.16.0_typescript@3.4.5 + typescript: 3.4.5 + specifiers: + '@pnpm/tslint-config': 0.0.0 + '@types/node': '*' + '@types/ramda': ^0.26.0 + '@types/semver': ^5.3.31 + '@types/tape': ^4.2.29 + '@zkochan/npm-package-arg': ^1.0.0 + better-path-resolve: 1.0.0 + pkgs-graph: 'link:' + ramda: ^0.26.0 + semver: ^6.0.0 + tape: ^4.6.3 + ts-node: ^8.0.3 + tslint: 5.16.0 + typescript: ^3.0.0 packages/pnpm: dependencies: '@pnpm/config': 'link:../config' @@ -1331,6 +1395,7 @@ importers: '@pnpm/logger': 2.1.0 '@pnpm/outdated': 'link:../outdated' '@pnpm/package-store': 'link:../package-store' + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' '@pnpm/server': 'link:../server' '@pnpm/store-controller-types': 'link:../store-controller-types' '@pnpm/store-path': 1.0.4 @@ -1351,7 +1416,7 @@ importers: diable: 4.0.2 dir-is-case-sensitive: 1.0.0 execa: 1.0.0 - find-packages: 4.0.1 + find-packages: 'link:../find-packages' get-port: 5.0.0 graceful-fs: 4.1.15 graph-sequencer: 2.0.0 @@ -1370,7 +1435,7 @@ importers: p-limit: 2.2.0 path-absolute: 1.0.1 path-name: 1.0.0 - pkgs-graph: 3.0.0 + pkgs-graph: 'link:../pkgs-graph' pnpm-file-reporter: 0.1.0 process-exists: 3.1.0 ramda: 0.26.1 @@ -1390,6 +1455,7 @@ importers: '@pnpm/prepare': 'link:../../privatePackages/prepare' '@pnpm/read-package-json': 'link:../read-package-json' '@pnpm/tslint-config': 'link:../../utils/tslint-config' + '@pnpm/write-importer-manifest': 'link:../write-importer-manifest' '@types/byline': 4.2.31 '@types/common-tags': 1.8.0 '@types/mkdirp': 0.5.2 @@ -1442,6 +1508,7 @@ importers: '@pnpm/outdated': 2.0.8 '@pnpm/package-store': 4.0.6 '@pnpm/prepare': 0.0.0 + '@pnpm/read-importer-manifest': 0.0.0 '@pnpm/read-package-json': 2.0.1 '@pnpm/server': 3.0.3 '@pnpm/store-controller-types': 3.0.3 @@ -1449,6 +1516,7 @@ importers: '@pnpm/tslint-config': 0.0.0 '@pnpm/types': 3.2.0 '@pnpm/utils': 0.10.4 + '@pnpm/write-importer-manifest': 0.0.0 '@types/archy': 0.0.31 '@types/byline': 4.2.31 '@types/common-tags': 1.8.0 @@ -1566,6 +1634,56 @@ importers: tslint: 5.16.0 typescript: 3.4.5 yaml-tag: 1.1.0 + packages/read-importer-manifest: + dependencies: + '@pnpm/types': 'link:../types' + '@pnpm/write-importer-manifest': 'link:../write-importer-manifest' + '@types/detect-indent': 5.0.0 + detect-indent: 5.0.0 + graceful-fs: 4.1.15 + is-windows: 1.0.2 + json5: 2.1.0 + read-yaml-file: 1.1.0 + strip-bom: 4.0.0 + devDependencies: + '@pnpm/read-importer-manifest': 'link:' + '@pnpm/tslint-config': 'link:../../utils/tslint-config' + '@types/graceful-fs': 4.1.3 + '@types/is-windows': 0.2.0 + '@types/json5': 0.0.30 + '@types/node': 10.14.6 + '@types/tape': 4.2.33 + mos: 2.0.0-alpha.3 + mos-plugin-readme: 1.0.4 + tape: 4.10.1 + tempy: 0.3.0 + ts-node: 8.1.0_typescript@3.4.5 + tslint: 5.16.0_typescript@3.4.5 + typescript: 3.4.5 + specifiers: + '@pnpm/read-importer-manifest': 'link:' + '@pnpm/tslint-config': 0.0.0 + '@pnpm/types': ^3.0.0 + '@pnpm/write-importer-manifest': 0.0.0 + '@types/detect-indent': 5.0.0 + '@types/graceful-fs': 4.1.3 + '@types/is-windows': 0.2.0 + '@types/json5': 0.0.30 + '@types/node': ^10.3.2 + '@types/tape': ^4.2.31 + detect-indent: 5.0.0 + graceful-fs: 4.1.15 + is-windows: 1.0.2 + json5: 2.1.0 + mos: ^2.0.0-alpha.3 + mos-plugin-readme: ^1.0.4 + read-yaml-file: 1.1.0 + strip-bom: 4.0.0 + tape: ^4.8.0 + tempy: 0.3.0 + ts-node: 8.1.0 + tslint: ^5.8.0 + typescript: 3.4.5 packages/read-importers-context: dependencies: '@pnpm/lockfile-file': 'link:../lockfile-file' @@ -1836,6 +1954,7 @@ importers: '@pnpm/package-requester': 'link:../package-requester' '@pnpm/pkgid-to-filename': 2.0.0 '@pnpm/prune-lockfile': 'link:../prune-lockfile' + '@pnpm/read-importer-manifest': 'link:../read-importer-manifest' '@pnpm/read-importers-context': 'link:../read-importers-context' '@pnpm/read-modules-dir': 'link:../read-modules-dir' '@pnpm/read-package-json': 'link:../read-package-json' @@ -1944,6 +2063,7 @@ importers: '@pnpm/pkgid-to-filename': 2.0.0 '@pnpm/prepare': 0.0.0 '@pnpm/prune-lockfile': 1.0.4 + '@pnpm/read-importer-manifest': 0.0.0 '@pnpm/read-importers-context': 1.0.1 '@pnpm/read-modules-dir': 2.0.0 '@pnpm/read-package-json': 2.0.1 @@ -2179,6 +2299,42 @@ importers: rimraf: 2.6.3 tslint: 5.16.0 typescript: 3.4.5 + packages/write-importer-manifest: + dependencies: + '@pnpm/types': 'link:../types' + sort-keys: 2.0.0 + write-json-file: 3.2.0 + write-json5-file: 2.0.0 + write-yaml-file: 2.0.0 + devDependencies: + '@pnpm/tslint-config': 'link:../../utils/tslint-config' + '@pnpm/write-importer-manifest': 'link:' + '@types/node': 10.14.6 + '@types/tape': 4.2.33 + mos: 2.0.0-alpha.3 + mos-plugin-readme: 1.0.4 + tape: 4.10.1 + tempy: 0.3.0 + ts-node: 8.1.0_typescript@3.4.5 + tslint: 5.16.0_typescript@3.4.5 + typescript: 3.4.5 + specifiers: + '@pnpm/tslint-config': 0.0.0 + '@pnpm/types': ^3.0.0 + '@pnpm/write-importer-manifest': 'link:' + '@types/node': ^10.3.2 + '@types/tape': ^4.2.31 + mos: ^2.0.0-alpha.3 + mos-plugin-readme: ^1.0.4 + sort-keys: 2.0.0 + tape: ^4.8.0 + tempy: 0.3.0 + ts-node: 8.1.0 + tslint: ^5.8.0 + typescript: 3.4.5 + write-json-file: 3.2.0 + write-json5-file: 2.0.0 + write-yaml-file: 2.0.0 privatePackages/assert-project: dependencies: '@pnpm/assert-store': 'link:../assert-store' @@ -2543,7 +2699,7 @@ packages: '@pnpm/types': 3.2.0 '@types/mz': 0.0.32 '@types/node': 11.13.8 - '@types/ramda': 0.26.8 + '@types/ramda': 0.26.6 '@zkochan/cmd-shim': 3.1.0 arr-flatten: 1.1.0 is-subdir: 1.0.3 @@ -2654,6 +2810,10 @@ packages: /@types/common-tags/1.8.0: resolution: integrity: sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg== + /@types/detect-indent/5.0.0: + dev: false + resolution: + integrity: sha512-eOFBOFCqusnH0i1s9XoURksqTZbsv5vbMMgOBKgSt67TsOw6ViaHO0Ii5DMhRoiU5TJ51uf9NsOpnxXsN96ywg== /@types/events/3.0.0: resolution: integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== @@ -2672,7 +2832,7 @@ packages: integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== /@types/graceful-fs/4.1.3: dependencies: - '@types/node': 11.13.8 + '@types/node': 10.14.6 resolution: integrity: sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ== /@types/http-proxy-agent/2.0.1: @@ -2689,6 +2849,10 @@ packages: dev: false resolution: integrity: sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA== + /@types/json5/0.0.30: + dev: true + resolution: + integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA== /@types/lru-cache/5.1.0: dev: false resolution: @@ -2718,6 +2882,12 @@ packages: dev: true resolution: integrity: sha512-DJM4sOEUeGzqjDkMtCubuN8zQ9gUxzPC+WVMzK2X4q6Kc5pnRFWcm8gkk4JNsDs7Zs6jjVf+dNYbNOii8fzPLA== + /@types/nock/10.0.1: + dependencies: + '@types/node': 11.13.8 + dev: true + resolution: + integrity: sha512-3Dbkj/f0HxuvyYfInbQCHLASFyjnNUcidabwrbhJDMZOXXznNyQpzsBgZnY2K+c43OekqWvZ+tDjGsGTKm1d5g== /@types/node-fetch/2.3.3: dependencies: '@types/node': 11.13.8 @@ -5051,17 +5221,6 @@ packages: node: '>= 0.8' resolution: integrity: sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== - /find-packages/4.0.1: - dependencies: - '@pnpm/read-package-json': 2.0.1 - '@types/node': 10.14.6 - fast-glob: 2.2.6 - p-filter: 2.1.0 - dev: false - engines: - node: '>=8' - resolution: - integrity: sha512-1qtGZL3BATaXYhYvH80CDgdy+5vVXLFGXMvJsJq00/E+/sIYd6BM/2V+hOq2VyR8Fe02k/v3pdl32TuLgyvSFg== /find-up/1.1.2: dependencies: path-exists: 2.1.0 @@ -6262,6 +6421,15 @@ packages: hasBin: true resolution: integrity: sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + /json5/2.1.0: + dependencies: + minimist: 1.2.0 + dev: false + engines: + node: '>=6' + hasBin: true + resolution: + integrity: sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== /jsondiffpatch/0.1.43: bundledDependencies: [] dependencies: @@ -7932,19 +8100,6 @@ packages: node: '>= 0.4.0' resolution: integrity: sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= - /pkgs-graph/3.0.0: - dependencies: - '@types/node': 11.13.8 - '@types/ramda': 0.26.8 - '@types/semver': 5.5.0 - '@zkochan/npm-package-arg': 1.0.2 - ramda: 0.26.1 - semver: 6.0.0 - dev: false - engines: - node: '>=8' - resolution: - integrity: sha512-GHjcGMs15Q3FtX9/aggZZuntrJpxSRXMYJNLsIveoaAZDGU89y7MBdaJr8PL9Qut71najIIzkfUHGmQZkWRWLA== /please-upgrade-node/3.1.1: dependencies: semver-compare: 1.0.0 @@ -9222,6 +9377,12 @@ packages: node: '>=4' resolution: integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + /strip-bom/4.0.0: + dev: false + engines: + node: '>=8' + resolution: + integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== /strip-color/0.1.0: dev: false engines: @@ -10112,6 +10273,18 @@ packages: node: '>=6' resolution: integrity: sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== + /write-json5-file/2.0.0: + dependencies: + graceful-fs: 4.1.15 + json5: 2.1.0 + mkdirp: 0.5.1 + sort-keys: 2.0.0 + write-file-atomic: 2.4.2 + dev: false + engines: + node: '>=8.15' + resolution: + integrity: sha512-/HE7Pf9lu94TWA8lAweK57xeNARpclDRNqOFeMfZto8VipnCB8IHnFF72aJocpjFsBRcVOgOe4KcrRyawAQ07A== /write-pkg/1.0.0: dependencies: write-json-file: 1.2.0