mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-30 13:02:03 -04:00
feat: add default-reporter
This commit is contained in:
11
packages/default-reporter/.editorconfig
Normal file
11
packages/default-reporter/.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[*.{ts,js,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
39
packages/default-reporter/.gitignore
vendored
Normal file
39
packages/default-reporter/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
lib
|
||||
2
packages/default-reporter/.npmrc
Normal file
2
packages/default-reporter/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
tag-version-prefix = pnpm-default-reporter/
|
||||
message = chore(release): %s
|
||||
15
packages/default-reporter/.travis.yml
Normal file
15
packages/default-reporter/.travis.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 4
|
||||
- 6
|
||||
- 8
|
||||
- 10
|
||||
sudo: false
|
||||
before_install:
|
||||
- curl -L https://unpkg.com/@pnpm/self-installer | node
|
||||
install:
|
||||
- pnpm install
|
||||
script:
|
||||
- npm test
|
||||
notifications:
|
||||
email: false
|
||||
21
packages/default-reporter/LICENSE
Normal file
21
packages/default-reporter/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Zoltan Kochan <z@kochan.io>
|
||||
|
||||
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.
|
||||
15
packages/default-reporter/README.md
Normal file
15
packages/default-reporter/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# pnpm-default-reporter
|
||||
|
||||
[](https://travis-ci.org/pnpm/pnpm-default-reporter "See test builds")
|
||||
|
||||
> The default reporter of pnpm
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install pnpm-default-reporter
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
93
packages/default-reporter/package.json
Normal file
93
packages/default-reporter/package.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"name": "pnpm-default-reporter",
|
||||
"version": "0.16.4",
|
||||
"description": "The default reporter of pnpm",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"bin": "lib/bin/pnpm-default-reporter.js",
|
||||
"scripts": {
|
||||
"lint": "tslint -c tslint.json --project .",
|
||||
"pretty-test": "preview && ts-node test | tap-diff",
|
||||
"test": "npm run lint && npm run just-test",
|
||||
"just-test": "preview && ts-node test --type-check",
|
||||
"tsc": "rimraf lib && tsc",
|
||||
"prepublishOnly": "npm run tsc"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pnpm/pnpm-reporter-default.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pnpm-reporter"
|
||||
],
|
||||
"author": {
|
||||
"name": "Zoltan Kochan",
|
||||
"email": "z@kochan.io",
|
||||
"url": "https://www.kochan.io/",
|
||||
"twitter": "ZoltanKochan"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm-reporter-default/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm-reporter-default#readme",
|
||||
"peerDependencies": {
|
||||
"supi": ">=0.16.0 <0.19.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/common-tags": "^1.2.5",
|
||||
"@types/node": "^9.3.0 || 10",
|
||||
"@types/ramda": "^0.25.6",
|
||||
"@types/semver": "^5.4.0",
|
||||
"@types/strip-ansi": "^3.0.0",
|
||||
"ansi-diff": "^1.0.10",
|
||||
"chalk": "^2.2.0",
|
||||
"cli-cursor": "^2.1.0",
|
||||
"common-tags": "^1.4.0",
|
||||
"most": "^1.7.2",
|
||||
"most-last": "^1.0.0",
|
||||
"ndjson": "^1.5.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"pretty-bytes": "^4.0.2",
|
||||
"ramda": "^0.25.0",
|
||||
"right-pad": "^1.0.1",
|
||||
"semver": "^5.4.1",
|
||||
"stacktracey": "^1.2.87",
|
||||
"string-length": "^2.0.0",
|
||||
"string.prototype.padstart": "^3.0.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"zen-push": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/logger": "^1.0.0",
|
||||
"@types/delay": "^2.0.1",
|
||||
"@types/tape": "^4.2.30",
|
||||
"commitizen": "^2.9.5",
|
||||
"delay": "^2.0.0",
|
||||
"ghooks": "^2.0.0",
|
||||
"mos-tap-diff": "^1.0.0",
|
||||
"normalize-newline": "^3.0.0",
|
||||
"package-preview": "^1.0.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"supi": "^0.18.0",
|
||||
"tape": "^4.8.0",
|
||||
"ts-node": "^6.0.0",
|
||||
"tslint": "^5.7.0",
|
||||
"typescript": "^2.6.2",
|
||||
"validate-commit-msg": "^2.8.2"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
},
|
||||
"ghooks": {
|
||||
"commit-msg": "node ./node_modules/validate-commit-msg/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/default-reporter/renovate.json
Normal file
6
packages/default-reporter/renovate.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"pinVersions": false
|
||||
}
|
||||
3800
packages/default-reporter/shrinkwrap.yaml
Normal file
3800
packages/default-reporter/shrinkwrap.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
import ndjson = require('ndjson')
|
||||
import reporter from '..'
|
||||
|
||||
process.stdin.resume()
|
||||
process.stdin.setEncoding('utf8')
|
||||
const streamParser = process.stdin
|
||||
.pipe(ndjson.parse())
|
||||
reporter(streamParser, {cmd: 'install'}) // TODO: make it smarter
|
||||
3
packages/default-reporter/src/constants.ts
Normal file
3
packages/default-reporter/src/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// In terminals, always Unix line endings are used
|
||||
// even on Windows
|
||||
export const EOL = '\n'
|
||||
158
packages/default-reporter/src/index.ts
Normal file
158
packages/default-reporter/src/index.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import createDiffer = require('ansi-diff')
|
||||
import cliCursor = require('cli-cursor')
|
||||
import most = require('most')
|
||||
import R = require('ramda')
|
||||
import * as supi from 'supi'
|
||||
import PushStream = require('zen-push')
|
||||
import {EOL} from './constants'
|
||||
import mergeOutputs from './mergeOutputs'
|
||||
import reporterForClient from './reporterForClient'
|
||||
import reporterForServer from './reporterForServer'
|
||||
|
||||
export default function (
|
||||
streamParser: object,
|
||||
opts: {
|
||||
cmd: string,
|
||||
cwd?: string,
|
||||
appendOnly?: boolean,
|
||||
throttleProgress?: number,
|
||||
width?: number,
|
||||
},
|
||||
) {
|
||||
if (opts.cmd === 'server') {
|
||||
const log$ = most.fromEvent<supi.Log>('data', streamParser)
|
||||
reporterForServer(log$)
|
||||
return
|
||||
}
|
||||
const width = opts.width || process.stdout.columns && process.stdout.columns - 2 || 80
|
||||
const output$ = toOutput$(streamParser, {...opts, width})
|
||||
if (opts.appendOnly) {
|
||||
output$
|
||||
.subscribe({
|
||||
complete () {}, // tslint:disable-line:no-empty
|
||||
error: (err) => console.error(err.message),
|
||||
next: (line) => console.log(line),
|
||||
})
|
||||
return
|
||||
}
|
||||
cliCursor.hide()
|
||||
const diff = createDiffer({
|
||||
height: process.stdout.rows,
|
||||
width,
|
||||
})
|
||||
output$
|
||||
.subscribe({
|
||||
complete () {}, // tslint:disable-line:no-empty
|
||||
error: (err) => logUpdate(err.message),
|
||||
next: logUpdate,
|
||||
})
|
||||
function logUpdate (view: string) {
|
||||
process.stdout.write(diff.update(`${view}${EOL}`))
|
||||
}
|
||||
}
|
||||
|
||||
export function toOutput$ (
|
||||
streamParser: object,
|
||||
opts: {
|
||||
cmd: string,
|
||||
cwd?: string,
|
||||
appendOnly?: boolean,
|
||||
throttleProgress?: number,
|
||||
width?: number,
|
||||
},
|
||||
): most.Stream<string> {
|
||||
opts = opts || {}
|
||||
const isRecursive = opts.cmd === 'recursive'
|
||||
const progressPushStream = new PushStream()
|
||||
const stagePushStream = new PushStream()
|
||||
const deprecationPushStream = new PushStream()
|
||||
const summaryPushStream = new PushStream()
|
||||
const lifecyclePushStream = new PushStream()
|
||||
const statsPushStream = new PushStream()
|
||||
const installCheckPushStream = new PushStream()
|
||||
const registryPushStream = new PushStream()
|
||||
const rootPushStream = new PushStream()
|
||||
const packageJsonPushStream = new PushStream()
|
||||
const linkPushStream = new PushStream()
|
||||
const cliPushStream = new PushStream()
|
||||
const otherPushStream = new PushStream()
|
||||
const hookPushStream = new PushStream()
|
||||
const skippedOptionalDependencyPushStream = new PushStream()
|
||||
setTimeout(() => { // setTimeout is a workaround for a strange bug in most https://github.com/cujojs/most/issues/491
|
||||
streamParser['on']('data', (log: supi.Log) => {
|
||||
switch (log.name) {
|
||||
case 'pnpm:progress':
|
||||
progressPushStream.next(log as supi.ProgressLog)
|
||||
break
|
||||
case 'pnpm:stage':
|
||||
stagePushStream.next(log as supi.StageLog)
|
||||
break
|
||||
case 'pnpm:deprecation':
|
||||
deprecationPushStream.next(log as supi.DeprecationLog)
|
||||
break
|
||||
case 'pnpm:summary':
|
||||
summaryPushStream.next(log)
|
||||
break
|
||||
case 'pnpm:lifecycle':
|
||||
lifecyclePushStream.next(log as supi.LifecycleLog)
|
||||
break
|
||||
case 'pnpm:stats':
|
||||
statsPushStream.next(log as supi.StatsLog)
|
||||
break
|
||||
case 'pnpm:install-check':
|
||||
installCheckPushStream.next(log as supi.InstallCheckLog)
|
||||
break
|
||||
case 'pnpm:registry':
|
||||
registryPushStream.next(log as supi.RegistryLog)
|
||||
break
|
||||
case 'pnpm:root':
|
||||
rootPushStream.next(log as supi.RootLog)
|
||||
break
|
||||
case 'pnpm:package-json':
|
||||
packageJsonPushStream.next(log as supi.PackageJsonLog)
|
||||
break
|
||||
case 'pnpm:link' as any: // tslint:disable-line
|
||||
linkPushStream.next(log)
|
||||
break
|
||||
case 'pnpm:cli' as any: // tslint:disable-line
|
||||
cliPushStream.next(log)
|
||||
break
|
||||
case 'pnpm:hook' as any: // tslint:disable-line
|
||||
hookPushStream.next(log)
|
||||
break
|
||||
case 'pnpm:skipped-optional-dependency':
|
||||
skippedOptionalDependencyPushStream.next(log as supi.SkippedOptionalDependencyLog)
|
||||
break
|
||||
case 'pnpm' as any: // tslint:disable-line
|
||||
otherPushStream.next(log)
|
||||
break
|
||||
}
|
||||
})
|
||||
}, 0)
|
||||
const log$ = {
|
||||
cli: most.from<supi.Log>(cliPushStream.observable),
|
||||
deprecation: most.from<supi.DeprecationLog>(deprecationPushStream.observable),
|
||||
hook: most.from<supi.Log>(hookPushStream.observable),
|
||||
installCheck: most.from<supi.InstallCheckLog>(installCheckPushStream.observable),
|
||||
lifecycle: most.from<supi.LifecycleLog>(lifecyclePushStream.observable),
|
||||
link: most.from<supi.Log>(linkPushStream.observable),
|
||||
other: most.from<supi.Log>(otherPushStream.observable),
|
||||
packageJson: most.from<supi.PackageJsonLog>(packageJsonPushStream.observable),
|
||||
progress: most.from<supi.ProgressLog>(progressPushStream.observable),
|
||||
registry: most.from<supi.RegistryLog>(registryPushStream.observable),
|
||||
root: most.from<supi.RootLog>(rootPushStream.observable),
|
||||
skippedOptionalDependency: most.from<supi.SkippedOptionalDependencyLog>(skippedOptionalDependencyPushStream.observable),
|
||||
stage: most.from<supi.StageLog>(stagePushStream.observable),
|
||||
stats: most.from<supi.StatsLog>(statsPushStream.observable),
|
||||
summary: most.from<supi.Log>(summaryPushStream.observable),
|
||||
}
|
||||
const outputs: Array<most.Stream<most.Stream<{msg: string}>>> = reporterForClient(log$, isRecursive, opts.cmd, opts.width, opts.appendOnly, opts.throttleProgress, opts.cwd)
|
||||
|
||||
if (opts.appendOnly) {
|
||||
return most.join(
|
||||
most.mergeArray(outputs)
|
||||
.map((log: most.Stream<{msg: string}>) => log.map((msg) => msg.msg)),
|
||||
)
|
||||
}
|
||||
return mergeOutputs(outputs).multicast()
|
||||
}
|
||||
67
packages/default-reporter/src/mergeOutputs.ts
Normal file
67
packages/default-reporter/src/mergeOutputs.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import most = require('most')
|
||||
import {EOL} from './constants'
|
||||
|
||||
export default function mergeOutputs (outputs: Array<most.Stream<most.Stream<{msg: string}>>>): most.Stream<string> {
|
||||
let blockNo = 0
|
||||
let fixedBlockNo = 0
|
||||
let started = false
|
||||
return most.join(
|
||||
most.mergeArray(outputs)
|
||||
.map((log: most.Stream<{msg: string}>) => {
|
||||
let currentBlockNo = -1
|
||||
let currentFixedBlockNo = -1
|
||||
return log
|
||||
.map((msg) => {
|
||||
if (msg['fixed']) {
|
||||
if (currentFixedBlockNo === -1) {
|
||||
currentFixedBlockNo = fixedBlockNo++
|
||||
}
|
||||
return {
|
||||
blockNo: currentFixedBlockNo,
|
||||
fixed: true,
|
||||
msg: msg.msg,
|
||||
}
|
||||
}
|
||||
if (currentBlockNo === -1) {
|
||||
currentBlockNo = blockNo++
|
||||
}
|
||||
return {
|
||||
blockNo: currentBlockNo,
|
||||
fixed: false,
|
||||
msg: typeof msg === 'string' ? msg : msg.msg,
|
||||
prevFixedBlockNo: currentFixedBlockNo,
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
.scan((acc, log) => {
|
||||
if (log.fixed === true) {
|
||||
acc.fixedBlocks[log.blockNo] = log.msg
|
||||
} else {
|
||||
delete acc.fixedBlocks[log['prevFixedBlockNo'] as number]
|
||||
acc.blocks[log.blockNo] = log.msg
|
||||
}
|
||||
return acc
|
||||
}, {fixedBlocks: [], blocks: []} as {fixedBlocks: string[], blocks: string[]})
|
||||
.map((sections) => {
|
||||
const fixedBlocks = sections.fixedBlocks.filter(Boolean)
|
||||
const nonFixedPart = sections.blocks.filter(Boolean).join(EOL)
|
||||
if (!fixedBlocks.length) {
|
||||
return nonFixedPart
|
||||
}
|
||||
const fixedPart = fixedBlocks.join(EOL)
|
||||
if (!nonFixedPart) {
|
||||
return fixedPart
|
||||
}
|
||||
return `${nonFixedPart}${EOL}${fixedPart}`
|
||||
})
|
||||
.filter((msg) => {
|
||||
if (started) {
|
||||
return true
|
||||
}
|
||||
if (msg === '') return false
|
||||
started = true
|
||||
return true
|
||||
})
|
||||
.skipRepeats()
|
||||
}
|
||||
148
packages/default-reporter/src/pkgsDiff.ts
Normal file
148
packages/default-reporter/src/pkgsDiff.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import most = require('most')
|
||||
import R = require('ramda')
|
||||
import {
|
||||
DeprecationLog,
|
||||
Log,
|
||||
} from 'supi'
|
||||
import * as supi from 'supi'
|
||||
|
||||
export interface PackageDiff {
|
||||
added: boolean,
|
||||
from?: string,
|
||||
name: string,
|
||||
realName?: string,
|
||||
version?: string,
|
||||
deprecated?: boolean,
|
||||
latest?: string,
|
||||
linked?: true,
|
||||
}
|
||||
|
||||
export interface Map<T> {
|
||||
[index: string]: T,
|
||||
}
|
||||
|
||||
export const propertyByDependencyType = {
|
||||
dev: 'devDependencies',
|
||||
optional: 'optionalDependencies',
|
||||
prod: 'dependencies',
|
||||
}
|
||||
|
||||
export default function (
|
||||
log$: {
|
||||
progress: most.Stream<supi.ProgressLog>,
|
||||
stage: most.Stream<supi.StageLog>,
|
||||
deprecation: most.Stream<supi.DeprecationLog>,
|
||||
summary: most.Stream<supi.Log>,
|
||||
lifecycle: most.Stream<supi.LifecycleLog>,
|
||||
stats: most.Stream<supi.StatsLog>,
|
||||
installCheck: most.Stream<supi.InstallCheckLog>,
|
||||
registry: most.Stream<supi.RegistryLog>,
|
||||
root: most.Stream<supi.RootLog>,
|
||||
packageJson: most.Stream<supi.PackageJsonLog>,
|
||||
link: most.Stream<supi.Log>,
|
||||
other: most.Stream<supi.Log>,
|
||||
},
|
||||
) {
|
||||
const deprecationSet$ = log$.deprecation
|
||||
.scan((acc, log) => {
|
||||
acc.add(log.pkgId)
|
||||
return acc
|
||||
}, new Set())
|
||||
|
||||
const pkgsDiff$ = most.combine(
|
||||
(rootLog, deprecationSet) => [rootLog, deprecationSet],
|
||||
log$.root,
|
||||
deprecationSet$,
|
||||
)
|
||||
.scan((pkgsDiff, args) => {
|
||||
const rootLog = args[0]
|
||||
const deprecationSet = args[1] as Set<string>
|
||||
if (rootLog['added']) {
|
||||
pkgsDiff[rootLog['added'].dependencyType][`+${rootLog['added'].name}`] = {
|
||||
added: true,
|
||||
deprecated: deprecationSet.has(rootLog['added'].id),
|
||||
latest: rootLog['added'].latest,
|
||||
name: rootLog['added'].name,
|
||||
realName: rootLog['added'].realName,
|
||||
version: rootLog['added'].version,
|
||||
}
|
||||
return pkgsDiff
|
||||
}
|
||||
if (rootLog['removed']) {
|
||||
pkgsDiff[rootLog['removed'].dependencyType][`-${rootLog['removed'].name}`] = {
|
||||
added: false,
|
||||
name: rootLog['removed'].name,
|
||||
version: rootLog['removed'].version,
|
||||
}
|
||||
return pkgsDiff
|
||||
}
|
||||
if (rootLog['linked']) {
|
||||
pkgsDiff[rootLog['linked'].dependencyType][`>${rootLog['linked'].name}`] = {
|
||||
added: false,
|
||||
from: rootLog['linked'].from,
|
||||
linked: true,
|
||||
name: rootLog['linked'].name,
|
||||
}
|
||||
return pkgsDiff
|
||||
}
|
||||
return pkgsDiff
|
||||
}, {
|
||||
dev: {},
|
||||
optional: {},
|
||||
prod: {},
|
||||
} as {
|
||||
dev: Map<PackageDiff>,
|
||||
prod: Map<PackageDiff>,
|
||||
optional: Map<PackageDiff>,
|
||||
})
|
||||
|
||||
const packageJson$ = most.fromPromise(
|
||||
most.merge(
|
||||
log$.packageJson,
|
||||
log$.summary.constant({}),
|
||||
)
|
||||
.take(2)
|
||||
.reduce(R.merge, {}),
|
||||
)
|
||||
|
||||
return most.combine(
|
||||
(pkgsDiff, packageJsons) => {
|
||||
const initialPackageJson = packageJsons['initial']
|
||||
const updatedPackageJson = packageJsons['updated']
|
||||
|
||||
if (!initialPackageJson || !updatedPackageJson) return pkgsDiff
|
||||
|
||||
for (const depType of ['prod', 'optional', 'dev']) {
|
||||
const prop = propertyByDependencyType[depType]
|
||||
const initialDeps = R.keys(initialPackageJson[prop])
|
||||
const updatedDeps = R.keys(updatedPackageJson[prop])
|
||||
const removedDeps = R.difference(initialDeps, updatedDeps)
|
||||
|
||||
for (const removedDep of removedDeps) {
|
||||
if (!pkgsDiff[depType][`-${removedDep}`]) {
|
||||
pkgsDiff[depType][`-${removedDep}`] = {
|
||||
added: false,
|
||||
name: removedDep,
|
||||
version: initialPackageJson[prop][removedDep],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const addedDeps = R.difference(updatedDeps, initialDeps)
|
||||
|
||||
for (const addedDep of addedDeps) {
|
||||
if (!pkgsDiff[depType][`+${addedDep}`]) {
|
||||
pkgsDiff[depType][`+${addedDep}`] = {
|
||||
added: true,
|
||||
name: addedDep,
|
||||
version: updatedPackageJson[prop][addedDep],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkgsDiff
|
||||
},
|
||||
pkgsDiff$,
|
||||
packageJson$,
|
||||
)
|
||||
}
|
||||
133
packages/default-reporter/src/reportError.ts
Normal file
133
packages/default-reporter/src/reportError.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import chalk from 'chalk'
|
||||
import commonTags = require('common-tags')
|
||||
import StackTracey = require('stacktracey')
|
||||
import {Log} from 'supi'
|
||||
import {EOL} from './constants'
|
||||
|
||||
const stripIndent = commonTags.stripIndent
|
||||
const stripIndents = commonTags.stripIndents
|
||||
const highlight = chalk.yellow
|
||||
const colorPath = chalk.gray
|
||||
|
||||
export default function reportError (logObj: Log) {
|
||||
if (logObj['err']) {
|
||||
const err = logObj['err'] as (Error & { code: string, stack: object })
|
||||
switch (err.code) {
|
||||
case 'UNEXPECTED_STORE':
|
||||
return reportUnexpectedStore(err, logObj['message'])
|
||||
case 'STORE_BREAKING_CHANGE':
|
||||
return reportStoreBreakingChange(err, logObj['message'])
|
||||
case 'MODULES_BREAKING_CHANGE':
|
||||
return reportModulesBreakingChange(err, logObj['message'])
|
||||
case 'MODIFIED_DEPENDENCY':
|
||||
return reportModifiedDependency(err, logObj['message'])
|
||||
case 'SHRINKWRAP_BREAKING_CHANGE':
|
||||
return reportShrinkwrapBreakingChange(err, logObj['message'])
|
||||
default:
|
||||
return formatGenericError(err.message || logObj['message'], err.stack)
|
||||
}
|
||||
}
|
||||
return formatErrorSummary(logObj['message'])
|
||||
}
|
||||
|
||||
function reportUnexpectedStore (err: Error, msg: object) {
|
||||
return stripIndent`
|
||||
${formatErrorSummary(err.message)}
|
||||
|
||||
expected: ${highlight(msg['expectedStorePath'])}
|
||||
actual: ${highlight(msg['actualStorePath'])}
|
||||
|
||||
If you want to use the new store, run the same command with the ${highlight('--force')} parameter.
|
||||
`
|
||||
}
|
||||
|
||||
function reportStoreBreakingChange (err: Error, msg: object) {
|
||||
let output = stripIndent`
|
||||
${formatErrorSummary(`The store used for the current node_modules is incomatible with the current version of pnpm`)}
|
||||
Store path: ${colorPath(msg['storePath'])}
|
||||
|
||||
Try running the same command with the ${highlight('--force')} parameter.
|
||||
`
|
||||
|
||||
if (msg['additionalInformation']) {
|
||||
output += EOL + EOL + msg['additionalInformation']
|
||||
}
|
||||
|
||||
output += formatRelatedSources(msg)
|
||||
return output
|
||||
}
|
||||
|
||||
function reportModulesBreakingChange (err: Error, msg: object) {
|
||||
let output = stripIndent`
|
||||
${formatErrorSummary(`The current version of pnpm is not compatible with the available node_modules structure`)}
|
||||
node_modules path: ${colorPath(msg['modulesPath'])}
|
||||
|
||||
Run ${highlight('pnpm install --force')} to recreate node_modules.
|
||||
`
|
||||
|
||||
if (msg['additionalInformation']) {
|
||||
output += EOL + EOL + msg['additionalInformation']
|
||||
}
|
||||
|
||||
output += formatRelatedSources(msg)
|
||||
return output
|
||||
}
|
||||
|
||||
function formatRelatedSources (msg: object) {
|
||||
let output = ''
|
||||
|
||||
if (!msg['relatedIssue'] && !msg['relatedPR']) return output
|
||||
|
||||
output += EOL
|
||||
|
||||
if (msg['relatedIssue']) {
|
||||
output += EOL + `Related issue: ${colorPath(`https://github.com/pnpm/pnpm/issues/${msg['relatedIssue']}`)}`
|
||||
}
|
||||
|
||||
if (msg['relatedPR']) {
|
||||
output += EOL + `Related PR: ${colorPath(`https://github.com/pnpm/pnpm/pull/${msg['relatedPR']}`)}`
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
function formatGenericError (errorMessage: string, stack: object) {
|
||||
if (stack) {
|
||||
let prettyStack: string | undefined
|
||||
try {
|
||||
prettyStack = new StackTracey(stack).pretty
|
||||
} catch (err) {
|
||||
prettyStack = undefined
|
||||
}
|
||||
if (prettyStack) {
|
||||
return stripIndents`
|
||||
${formatErrorSummary(errorMessage)}
|
||||
${prettyStack}
|
||||
`
|
||||
}
|
||||
}
|
||||
return formatErrorSummary(errorMessage)
|
||||
}
|
||||
|
||||
function formatErrorSummary (message: string) {
|
||||
return `${chalk.bgRed.black('\u2009ERROR\u2009')} ${chalk.red(message)}`
|
||||
}
|
||||
|
||||
function reportModifiedDependency (err: Error, msg: object) {
|
||||
return stripIndent`
|
||||
${formatErrorSummary('Packages in the store have been mutated')}
|
||||
|
||||
These packages are modified:
|
||||
${msg['modified'].map((pkgPath: string) => colorPath(pkgPath)).join(EOL)}
|
||||
|
||||
You can run ${highlight('pnpm install')} to refetch the modified packages
|
||||
`
|
||||
}
|
||||
|
||||
function reportShrinkwrapBreakingChange (err: Error, msg: object) {
|
||||
return stripIndent`
|
||||
${formatErrorSummary(err.message)}
|
||||
|
||||
Run with the ${highlight('--force')} parameter to recreate the shrinkwrap file.
|
||||
`
|
||||
}
|
||||
533
packages/default-reporter/src/reporterForClient.ts
Normal file
533
packages/default-reporter/src/reporterForClient.ts
Normal file
@@ -0,0 +1,533 @@
|
||||
import chalk from 'chalk'
|
||||
import most = require('most')
|
||||
import {last as mostLast} from 'most-last'
|
||||
import normalize = require('normalize-path')
|
||||
import os = require('os')
|
||||
import path = require('path')
|
||||
import prettyBytes = require('pretty-bytes')
|
||||
import R = require('ramda')
|
||||
import rightPad = require('right-pad')
|
||||
import semver = require('semver')
|
||||
import stringLength = require('string-length')
|
||||
import padStart = require('string.prototype.padstart')
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import {
|
||||
DeprecationLog,
|
||||
InstallCheckLog,
|
||||
LifecycleLog,
|
||||
Log,
|
||||
ProgressLog,
|
||||
RegistryLog,
|
||||
} from 'supi'
|
||||
import * as supi from 'supi'
|
||||
import PushStream = require('zen-push')
|
||||
import {EOL} from './constants'
|
||||
import getPkgsDiff, {
|
||||
PackageDiff,
|
||||
propertyByDependencyType,
|
||||
} from './pkgsDiff'
|
||||
import reportError from './reportError'
|
||||
|
||||
const BIG_TARBALL_SIZE = 1024 * 1024 * 5 // 5 MB
|
||||
|
||||
const ADDED_CHAR = chalk.green('+')
|
||||
const REMOVED_CHAR = chalk.red('-')
|
||||
const LINKED_CHAR = chalk.magentaBright('#')
|
||||
const PREFIX_MAX_LENGTH = 40
|
||||
|
||||
const hlValue = chalk.blue
|
||||
const hlPkgId = chalk['whiteBright']
|
||||
|
||||
export default function (
|
||||
log$: {
|
||||
progress: most.Stream<supi.ProgressLog>,
|
||||
stage: most.Stream<supi.StageLog>,
|
||||
deprecation: most.Stream<supi.DeprecationLog>,
|
||||
summary: most.Stream<supi.Log>,
|
||||
lifecycle: most.Stream<supi.LifecycleLog>,
|
||||
stats: most.Stream<supi.StatsLog>,
|
||||
installCheck: most.Stream<supi.InstallCheckLog>,
|
||||
registry: most.Stream<supi.RegistryLog>,
|
||||
root: most.Stream<supi.RootLog>,
|
||||
packageJson: most.Stream<supi.PackageJsonLog>,
|
||||
link: most.Stream<supi.Log>,
|
||||
other: most.Stream<supi.Log>,
|
||||
cli: most.Stream<supi.Log>,
|
||||
hook: most.Stream<supi.Log>,
|
||||
skippedOptionalDependency: most.Stream<supi.SkippedOptionalDependencyLog>,
|
||||
},
|
||||
isRecursive: boolean,
|
||||
cmd: string,
|
||||
widthArg?: number,
|
||||
appendOnly?: boolean,
|
||||
throttleProgress?: number,
|
||||
cwdArg?: string,
|
||||
): Array<most.Stream<most.Stream<{msg: string}>>> {
|
||||
const width = widthArg || process.stdout.columns || 80
|
||||
const outputs: Array<most.Stream<most.Stream<{msg: string}>>> = []
|
||||
const cwd = cwdArg || process.cwd()
|
||||
|
||||
const resolutionDone$ = isRecursive
|
||||
? most.never()
|
||||
: log$.stage
|
||||
.filter((log) => log.message === 'resolution_done')
|
||||
|
||||
const resolvingContentLog$ = log$.progress
|
||||
.filter((log) => log.status === 'resolving_content')
|
||||
.scan(R.inc, 0)
|
||||
.skip(1)
|
||||
.until(resolutionDone$)
|
||||
|
||||
const fedtchedLog$ = log$.progress
|
||||
.filter((log) => log.status === 'fetched')
|
||||
.scan(R.inc, 0)
|
||||
|
||||
const foundInStoreLog$ = log$.progress
|
||||
.filter((log) => log.status === 'found_in_store')
|
||||
.scan(R.inc, 0)
|
||||
|
||||
function createStatusMessage (resolving: number, fetched: number, foundInStore: number, importingDone: boolean) {
|
||||
const msg = `Resolving: total ${hlValue(resolving.toString())}, reused ${hlValue(foundInStore.toString())}, downloaded ${hlValue(fetched.toString())}`
|
||||
if (importingDone) {
|
||||
return {
|
||||
done: true,
|
||||
fixed: false,
|
||||
msg: `${msg}, done`,
|
||||
}
|
||||
}
|
||||
return {
|
||||
fixed: true,
|
||||
msg,
|
||||
}
|
||||
}
|
||||
|
||||
const importingDone$ = log$.stage.filter((log) => log.message === 'importing_done')
|
||||
.constant(true)
|
||||
.take(1)
|
||||
.startWith(false)
|
||||
.multicast()
|
||||
|
||||
if (!isRecursive && typeof throttleProgress === 'number' && throttleProgress > 0) {
|
||||
const resolutionStarted$ = log$.stage
|
||||
.filter((log) => log.message === 'resolution_started' || log.message === 'importing_started').take(1)
|
||||
const commandDone$ = log$.cli.filter((log) => log['message'] === 'command_done')
|
||||
|
||||
// Reporting is done every `throttleProgress` milliseconds
|
||||
// and once all packages are fetched.
|
||||
const sampler = most.merge(
|
||||
most.periodic(throttleProgress).since(resolutionStarted$).until(most.merge<{}>(importingDone$.skip(1), commandDone$)),
|
||||
importingDone$,
|
||||
)
|
||||
const progress = most.sample(
|
||||
createStatusMessage,
|
||||
sampler,
|
||||
resolvingContentLog$,
|
||||
fedtchedLog$,
|
||||
foundInStoreLog$,
|
||||
importingDone$,
|
||||
)
|
||||
// Avoid logs after all resolved packages were downloaded.
|
||||
// Fixing issue: https://github.com/pnpm/pnpm/issues/1028#issuecomment-364782901
|
||||
.skipAfter((msg) => msg.done === true)
|
||||
|
||||
outputs.push(most.of(progress))
|
||||
} else {
|
||||
const progress = most.combine(
|
||||
createStatusMessage,
|
||||
resolvingContentLog$,
|
||||
fedtchedLog$,
|
||||
foundInStoreLog$,
|
||||
isRecursive ? most.of(false) : importingDone$,
|
||||
)
|
||||
outputs.push(most.of(progress))
|
||||
}
|
||||
|
||||
if (!appendOnly) {
|
||||
const tarballsProgressOutput$ = log$.progress
|
||||
.filter((log) => log.status === 'fetching_started' &&
|
||||
typeof log.size === 'number' && log.size >= BIG_TARBALL_SIZE &&
|
||||
// When retrying the download, keep the existing progress line.
|
||||
// Fixing issue: https://github.com/pnpm/pnpm/issues/1013
|
||||
log.attempt === 1)
|
||||
.map((startedLog) => {
|
||||
const size = prettyBytes(startedLog['size'])
|
||||
return log$.progress
|
||||
.filter((log) => log.status === 'fetching_progress' && log.pkgId === startedLog['pkgId'])
|
||||
.map((log) => log['downloaded'])
|
||||
.startWith(0)
|
||||
.map((downloadedRaw) => {
|
||||
const done = startedLog['size'] === downloadedRaw
|
||||
const downloaded = prettyBytes(downloadedRaw)
|
||||
return {
|
||||
fixed: !done,
|
||||
msg: `Downloading ${hlPkgId(startedLog['pkgId'])}: ${hlValue(downloaded)}/${hlValue(size)}${done ? ', done' : ''}`,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
outputs.push(tarballsProgressOutput$)
|
||||
|
||||
const lifecycleMessages: {
|
||||
[depPath: string]: {
|
||||
output: string[],
|
||||
script: string,
|
||||
},
|
||||
} = {}
|
||||
const lifecycleStreamByDepPath: {
|
||||
[depPath: string]: {
|
||||
observable: most.Observable<{msg: string}>,
|
||||
complete (): void,
|
||||
next (obj: object): void,
|
||||
},
|
||||
} = {}
|
||||
const lifecyclePushStream = new PushStream()
|
||||
outputs.push(most.from(lifecyclePushStream.observable))
|
||||
|
||||
log$.lifecycle
|
||||
.forEach((log: LifecycleLog) => {
|
||||
const key = `${log.stage}:${log.depPath}`
|
||||
lifecycleMessages[key] = lifecycleMessages[key] || {output: []}
|
||||
if (log['script']) {
|
||||
lifecycleMessages[key].script = formatLifecycle(cwd, log)
|
||||
} else {
|
||||
if (!lifecycleMessages[key].output.length || log['exitCode'] !== 0) {
|
||||
lifecycleMessages[key].output.push(formatLifecycle(cwd, log))
|
||||
}
|
||||
if (lifecycleMessages[key].output.length > 3) {
|
||||
lifecycleMessages[key].output.shift()
|
||||
}
|
||||
}
|
||||
if (!lifecycleStreamByDepPath[key]) {
|
||||
lifecycleStreamByDepPath[key] = new PushStream()
|
||||
lifecyclePushStream.next(most.from(lifecycleStreamByDepPath[key].observable))
|
||||
}
|
||||
lifecycleStreamByDepPath[key].next({
|
||||
msg: EOL + [lifecycleMessages[key].script].concat(lifecycleMessages[key].output).join(EOL),
|
||||
})
|
||||
if (typeof log['exitCode'] === 'number') {
|
||||
lifecycleStreamByDepPath[key].complete()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const lifecycleMessages: {[pkgId: string]: string} = {}
|
||||
const lifecycleOutput$ = most.of(
|
||||
log$.lifecycle
|
||||
.map((log: LifecycleLog) => ({ msg: formatLifecycle(cwd, log) })),
|
||||
)
|
||||
|
||||
outputs.push(lifecycleOutput$)
|
||||
}
|
||||
|
||||
if (!isRecursive) {
|
||||
const pkgsDiff$ = getPkgsDiff(log$)
|
||||
|
||||
const summaryLog$ = log$.summary
|
||||
.take(1)
|
||||
|
||||
const summaryOutput$ = most.combine(
|
||||
(pkgsDiff) => {
|
||||
let msg = ''
|
||||
for (const depType of ['prod', 'optional', 'dev']) {
|
||||
const diffs = R.values(pkgsDiff[depType])
|
||||
if (diffs.length) {
|
||||
msg += EOL
|
||||
msg += chalk.blue(`${propertyByDependencyType[depType]}:`)
|
||||
msg += EOL
|
||||
msg += printDiffs(diffs)
|
||||
msg += EOL
|
||||
}
|
||||
}
|
||||
return {msg}
|
||||
},
|
||||
pkgsDiff$,
|
||||
summaryLog$,
|
||||
)
|
||||
.take(1)
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(summaryOutput$)
|
||||
|
||||
const deprecationOutput$ = log$.deprecation
|
||||
// print warnings only about deprecated packages from the root
|
||||
.filter((log) => log.depth === 0)
|
||||
.map((log) => {
|
||||
return {
|
||||
msg: formatWarn(`${chalk.red('deprecated')} ${log.pkgName}@${log.pkgVersion}: ${log.deprecated}`),
|
||||
}
|
||||
})
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(deprecationOutput$)
|
||||
}
|
||||
|
||||
if (!isRecursive) {
|
||||
outputs.push(
|
||||
most.fromPromise(
|
||||
log$.stats
|
||||
.take((cmd === 'install' || cmd === 'update') ? 2 : 1)
|
||||
.reduce((acc, log) => {
|
||||
if (typeof log['added'] === 'number') {
|
||||
acc['added'] = log['added']
|
||||
} else if (typeof log['removed'] === 'number') {
|
||||
acc['removed'] = log['removed']
|
||||
}
|
||||
return acc
|
||||
}, {}),
|
||||
)
|
||||
.map((stats) => {
|
||||
if (!stats['removed'] && !stats['added']) {
|
||||
return most.of({msg: 'Already up-to-date'})
|
||||
}
|
||||
|
||||
let msg = 'Packages:'
|
||||
if (stats['added']) {
|
||||
msg += ' ' + chalk.green(`+${stats['added']}`)
|
||||
}
|
||||
if (stats['removed']) {
|
||||
msg += ' ' + chalk.red(`-${stats['removed']}`)
|
||||
}
|
||||
msg += EOL + printPlusesAndMinuses(width, (stats['added'] || 0), (stats['removed'] || 0))
|
||||
return most.of({msg})
|
||||
}),
|
||||
)
|
||||
|
||||
const installCheckOutput$ = log$.installCheck
|
||||
.map(formatInstallCheck)
|
||||
.filter(Boolean)
|
||||
.map((msg) => ({msg}))
|
||||
.map(most.of) as most.Stream<most.Stream<{msg: string}>>
|
||||
|
||||
outputs.push(installCheckOutput$)
|
||||
|
||||
const registryOutput$ = log$.registry
|
||||
.filter((log) => log.level === 'warn')
|
||||
.map((log: RegistryLog) => ({msg: formatWarn(log.message)}))
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(registryOutput$)
|
||||
|
||||
const miscOutput$ = most.merge(log$.link, log$.other)
|
||||
.map((obj) => {
|
||||
if (obj.level === 'debug') return
|
||||
if (obj.level === 'warn') {
|
||||
return formatWarn(obj['message'])
|
||||
}
|
||||
if (obj.level === 'error') {
|
||||
return reportError(obj)
|
||||
}
|
||||
return obj['message']
|
||||
})
|
||||
.map((msg) => ({msg}))
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(miscOutput$)
|
||||
|
||||
outputs.push(
|
||||
log$.skippedOptionalDependency
|
||||
.filter((log) => Boolean(log.parents && log.parents.length === 0))
|
||||
.map((log) => most.of({
|
||||
msg: `info: ${
|
||||
log.package['id'] || log.package.name && (`${log.package.name}@${log.package.version}`) || log.package['pref']
|
||||
} is an optional dependency and failed compatibility check. Excluding it from installation.`,
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
outputs.push(
|
||||
log$.stats
|
||||
.loop((stats, log) => {
|
||||
if (stats[log.prefix]) {
|
||||
const value = {...stats[log.prefix], ...log}
|
||||
delete stats[log.prefix]
|
||||
return {seed: stats, value}
|
||||
}
|
||||
stats[log.prefix] = log
|
||||
return {seed: stats, value: null}
|
||||
}, {})
|
||||
.filter((stats) => stats !== null && (stats['removed'] || stats['added']))
|
||||
.map((stats) => {
|
||||
const prefix = formatPrefix(cwd, stats['prefix'])
|
||||
|
||||
let msg = `${rightPad(prefix, PREFIX_MAX_LENGTH)} |`
|
||||
|
||||
if (stats['added']) {
|
||||
msg += ` ${padStep(chalk.green(`+${stats['added']}`), 4)}`
|
||||
}
|
||||
if (stats['removed']) {
|
||||
msg += ` ${padStep(chalk.red(`-${stats['removed']}`), 4)}`
|
||||
}
|
||||
|
||||
const rest = Math.max(0, width - 1 - stringLength(msg))
|
||||
msg += ' ' + printPlusesAndMinuses(rest, roundStats(stats['added'] || 0), roundStats(stats['removed'] || 0))
|
||||
return most.of({msg})
|
||||
}),
|
||||
)
|
||||
|
||||
const miscOutput$ = log$.other
|
||||
.filter((obj) => obj.level === 'error')
|
||||
.map((obj) => {
|
||||
if (obj['message']['prefix']) {
|
||||
return obj['message']['prefix'] + ':' + os.EOL + reportError(obj)
|
||||
}
|
||||
return reportError(obj)
|
||||
})
|
||||
.map((msg) => ({msg}))
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(miscOutput$)
|
||||
}
|
||||
|
||||
if (!isRecursive) {
|
||||
const hookOutput$ = log$.hook
|
||||
.map((log) => ({msg: `${chalk.magentaBright(log['hook'])}: ${log['message']}`}))
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(hookOutput$)
|
||||
} else {
|
||||
const hookOutput$ = log$.hook
|
||||
.map((log) => ({
|
||||
msg: `${rightPad(formatPrefix(cwd, log['prefix']), PREFIX_MAX_LENGTH)} | ${chalk.magentaBright(log['hook'])}: ${log['message']}`,
|
||||
}))
|
||||
.map(most.of)
|
||||
|
||||
outputs.push(hookOutput$)
|
||||
}
|
||||
|
||||
return outputs
|
||||
}
|
||||
|
||||
function padStep (s: string, step: number) {
|
||||
const sLength = stringLength(s)
|
||||
const placeholderLength = Math.ceil(sLength / step) * step
|
||||
if (sLength < placeholderLength) {
|
||||
return R.repeat(' ', placeholderLength - sLength).join('') + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
function roundStats (stat: number): number {
|
||||
if (stat === 0) return 0
|
||||
return Math.max(1, Math.round(stat / 10))
|
||||
}
|
||||
|
||||
function formatPrefix (cwd: string, prefix: string) {
|
||||
prefix = normalize(path.relative(cwd, prefix) || '.')
|
||||
|
||||
if (prefix.length <= PREFIX_MAX_LENGTH) {
|
||||
return prefix
|
||||
}
|
||||
|
||||
const shortPrefix = prefix.substr(-PREFIX_MAX_LENGTH + 3)
|
||||
|
||||
const separatorLocation = shortPrefix.indexOf('/')
|
||||
|
||||
if (separatorLocation <= 0) {
|
||||
return `...${shortPrefix}`
|
||||
}
|
||||
|
||||
return `...${shortPrefix.substr(separatorLocation)}`
|
||||
}
|
||||
|
||||
function printPlusesAndMinuses (maxWidth: number, added: number, removed: number) {
|
||||
if (maxWidth === 0) return ''
|
||||
const changes = added + removed
|
||||
let addedChars: number
|
||||
let removedChars: number
|
||||
if (changes > maxWidth) {
|
||||
if (!added) {
|
||||
addedChars = 0
|
||||
removedChars = maxWidth
|
||||
} else if (!removed) {
|
||||
addedChars = maxWidth
|
||||
removedChars = 0
|
||||
} else {
|
||||
const p = maxWidth / changes
|
||||
addedChars = Math.min(Math.max(Math.floor(added * p), 1), maxWidth - 1)
|
||||
removedChars = maxWidth - addedChars
|
||||
}
|
||||
} else {
|
||||
addedChars = added
|
||||
removedChars = removed
|
||||
}
|
||||
return `${R.repeat(ADDED_CHAR, addedChars).join('')}${R.repeat(REMOVED_CHAR, removedChars).join('')}`
|
||||
}
|
||||
|
||||
function printDiffs (pkgsDiff: PackageDiff[]) {
|
||||
// Sorts by alphabet then by removed/added
|
||||
// + ava 0.10.0
|
||||
// - chalk 1.0.0
|
||||
// + chalk 2.0.0
|
||||
pkgsDiff.sort((a, b) => (a.name.localeCompare(b.name) * 10 + (Number(!b.added) - Number(!a.added))))
|
||||
const msg = pkgsDiff.map((pkg) => {
|
||||
let result = pkg.added
|
||||
? ADDED_CHAR
|
||||
: pkg.linked
|
||||
? LINKED_CHAR
|
||||
: REMOVED_CHAR
|
||||
if (!pkg.realName || pkg.name === pkg.realName) {
|
||||
result += ` ${pkg.name}`
|
||||
} else {
|
||||
result += ` ${pkg.name} <- ${pkg.realName}`
|
||||
}
|
||||
if (pkg.version) {
|
||||
result += ` ${chalk.grey(pkg.version)}`
|
||||
if (pkg.latest && semver.lt(pkg.version, pkg.latest)) {
|
||||
result += ` ${chalk.grey(`(${pkg.latest} is available)`)}`
|
||||
}
|
||||
}
|
||||
if (pkg.deprecated) {
|
||||
result += ` ${chalk.red('deprecated')}`
|
||||
}
|
||||
if (pkg.linked) {
|
||||
result += ` ${chalk.magentaBright('linked from')} ${chalk.grey(pkg.from || '???')}`
|
||||
}
|
||||
return result
|
||||
}).join(EOL)
|
||||
return msg
|
||||
}
|
||||
|
||||
function formatLifecycle (cwd: string, logObj: LifecycleLog) {
|
||||
const prefix = `${
|
||||
logObj.wd === logObj.depPath
|
||||
? rightPad(formatPrefix(cwd, logObj.wd), PREFIX_MAX_LENGTH)
|
||||
: rightPad(logObj.depPath, PREFIX_MAX_LENGTH)
|
||||
} | ${hlValue(padStart(logObj.stage, 11))}`
|
||||
if (logObj['script']) {
|
||||
return `${prefix}$ ${logObj['script']}`
|
||||
}
|
||||
if (logObj['exitCode'] === 0) {
|
||||
return `${prefix}: done`
|
||||
}
|
||||
const line = formatLine(logObj)
|
||||
if (logObj.level === 'error') {
|
||||
return `${prefix}: ${line}`
|
||||
}
|
||||
return `${prefix}: ${line}`
|
||||
}
|
||||
|
||||
function formatLine (logObj: LifecycleLog) {
|
||||
if (typeof logObj['exitCode'] === 'number') return chalk.red(`Exited with ${logObj['exitCode']}`)
|
||||
|
||||
// TODO: strip only the non-color/style ansi escape codes
|
||||
if (logObj.level === 'error') {
|
||||
return chalk.gray(stripAnsi(logObj['line']))
|
||||
}
|
||||
return stripAnsi(logObj['line'])
|
||||
}
|
||||
|
||||
function formatInstallCheck (logObj: InstallCheckLog) {
|
||||
switch (logObj.code) {
|
||||
case 'EBADPLATFORM':
|
||||
return formatWarn(`Unsupported system. Skipping dependency ${logObj.pkgId}`)
|
||||
case 'ENOTSUP':
|
||||
return logObj.toString()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function formatWarn (message: string) {
|
||||
// The \u2009 is the "thin space" unicode character
|
||||
// It is used instead of ' ' because chalk (as of version 2.1.0)
|
||||
// trims whitespace at the beginning
|
||||
return `${chalk.bgYellow.black('\u2009WARN\u2009')} ${message}`
|
||||
}
|
||||
57
packages/default-reporter/src/reporterForServer.ts
Normal file
57
packages/default-reporter/src/reporterForServer.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import chalk from 'chalk'
|
||||
import most = require('most')
|
||||
import prettyBytes = require('pretty-bytes')
|
||||
import R = require('ramda')
|
||||
import semver = require('semver')
|
||||
import {
|
||||
DeprecationLog,
|
||||
InstallCheckLog,
|
||||
LifecycleLog,
|
||||
Log,
|
||||
ProgressLog,
|
||||
RegistryLog,
|
||||
} from 'supi'
|
||||
import getPkgsDiff, {
|
||||
PackageDiff,
|
||||
propertyByDependencyType,
|
||||
} from './pkgsDiff'
|
||||
import reportError from './reportError'
|
||||
|
||||
export default function (
|
||||
log$: most.Stream<Log>,
|
||||
) {
|
||||
log$.subscribe({
|
||||
complete: () => undefined,
|
||||
error: () => undefined,
|
||||
next (log) {
|
||||
if (log.name === 'pnpm:progress') {
|
||||
switch (log.status) {
|
||||
case 'fetched':
|
||||
case 'fetching_started':
|
||||
console.log(`${chalk.cyan(log.status)} ${log.pkgId}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
switch (log.level) {
|
||||
case 'warn':
|
||||
console.log(formatWarn(log['message']))
|
||||
return
|
||||
case 'error':
|
||||
console.log(reportError(log))
|
||||
return
|
||||
case 'debug':
|
||||
return
|
||||
default:
|
||||
console.log(log['message'])
|
||||
return
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function formatWarn (message: string) {
|
||||
// The \u2009 is the "thin space" unicode character
|
||||
// It is used instead of ' ' because chalk (as of version 2.1.0)
|
||||
// trims whitespace at the beginning
|
||||
return `${chalk.bgYellow.black('\u2009WARN\u2009')} ${message}`
|
||||
}
|
||||
997
packages/default-reporter/test/index.ts
Normal file
997
packages/default-reporter/test/index.ts
Normal file
@@ -0,0 +1,997 @@
|
||||
import logger, {
|
||||
createStreamParser,
|
||||
} from '@pnpm/logger'
|
||||
import delay = require('delay')
|
||||
import test = require('tape')
|
||||
import normalizeNewline = require('normalize-newline')
|
||||
import {toOutput$} from 'pnpm-default-reporter'
|
||||
import {stripIndents} from 'common-tags'
|
||||
import chalk from 'chalk'
|
||||
import most = require('most')
|
||||
import StackTracey = require('stacktracey')
|
||||
import R = require('ramda')
|
||||
|
||||
const WARN = chalk.bgYellow.black('\u2009WARN\u2009')
|
||||
const ERROR = chalk.bgRed.black('\u2009ERROR\u2009')
|
||||
const DEPRECATED = chalk.red('deprecated')
|
||||
const versionColor = chalk.grey
|
||||
const ADD = chalk.green('+')
|
||||
const SUB = chalk.red('-')
|
||||
const LINKED = chalk.magentaBright('#')
|
||||
const h1 = chalk.blue
|
||||
const hlValue = chalk.blue
|
||||
const hlPkgId = chalk['whiteBright']
|
||||
const POSTINSTALL = hlValue('postinstall')
|
||||
const PREINSTALL = hlValue(' preinstall')
|
||||
const INSTALL = hlValue(' install')
|
||||
|
||||
const progressLogger = logger<object>('progress')
|
||||
const stageLogger = logger<string>('stage')
|
||||
const rootLogger = logger<object>('root')
|
||||
const deprecationLogger = logger<object>('deprecation')
|
||||
const summaryLogger = logger<object>('summary')
|
||||
const lifecycleLogger = logger<object>('lifecycle')
|
||||
const packageJsonLogger = logger<object>('package-json')
|
||||
const statsLogger = logger<object>('stats')
|
||||
const hookLogger = logger<object>('hook')
|
||||
const skippedOptionalDependencyLogger = logger<object>('skipped-optional-dependency')
|
||||
const EOL = '\n'
|
||||
|
||||
test('prints progress beginning', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId,
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, `Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}`)
|
||||
},
|
||||
error: t.end,
|
||||
complete: () => t.end(),
|
||||
})
|
||||
})
|
||||
|
||||
test('prints progress beginning when appendOnly is true', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', appendOnly: true})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId,
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, `Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}`)
|
||||
},
|
||||
error: t.end,
|
||||
complete: () => t.end(),
|
||||
})
|
||||
})
|
||||
|
||||
test('prints progress beginning during recursive install', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive'})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId,
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, `Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}`)
|
||||
},
|
||||
error: t.end,
|
||||
complete: () => t.end(),
|
||||
})
|
||||
})
|
||||
|
||||
test('prints progress on first download', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', throttleProgress: 0})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId,
|
||||
})
|
||||
progressLogger.debug({
|
||||
status: 'fetched',
|
||||
pkgId,
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.skip(1).take(1).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, `Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('1')}`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('moves fixed line to the end', async t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', throttleProgress: 0})
|
||||
|
||||
output$.skip(3).take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
${WARN} foo
|
||||
Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('1')}, done
|
||||
`)
|
||||
},
|
||||
complete: v => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId,
|
||||
})
|
||||
progressLogger.debug({
|
||||
status: 'fetched',
|
||||
pkgId,
|
||||
})
|
||||
logger.warn('foo')
|
||||
|
||||
await delay(0) // w/o delay warning goes below for some reason. Started to happen after switch to most
|
||||
|
||||
stageLogger.debug('resolution_done')
|
||||
stageLogger.debug('importing_done')
|
||||
|
||||
t.plan(1)
|
||||
})
|
||||
|
||||
test('prints "Already up-to-date"', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
statsLogger.debug({ added: 0 })
|
||||
statsLogger.debug({ removed: 0 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Already up-to-date
|
||||
`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints summary', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
packageJsonLogger.debug({
|
||||
initial: {
|
||||
dependencies: {
|
||||
'is-13': '^1.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'is-negative': '^1.0.0',
|
||||
},
|
||||
},
|
||||
})
|
||||
deprecationLogger.warn({
|
||||
pkgName: 'bar',
|
||||
pkgVersion: '2.0.0',
|
||||
pkgId: 'registry.npmjs.org/bar/2.0.0',
|
||||
deprecated: 'This package was deprecated because bla bla bla',
|
||||
depth: 0,
|
||||
})
|
||||
rootLogger.info({
|
||||
added: {
|
||||
dependencyType: 'prod',
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
latest: '2.0.0',
|
||||
id: 'registry.npmjs.org/foo/1.0.0',
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
added: {
|
||||
dependencyType: 'prod',
|
||||
name: 'bar',
|
||||
version: '2.0.0',
|
||||
latest: '1.0.0', // this won't be printed in summary because latest is less than current version
|
||||
id: 'registry.npmjs.org/bar/2.0.0',
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
removed: {
|
||||
dependencyType: 'prod',
|
||||
name: 'foo',
|
||||
version: '0.1.0',
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
added: {
|
||||
dependencyType: 'dev',
|
||||
name: 'qar',
|
||||
version: '2.0.0',
|
||||
id: 'registry.npmjs.org/qar/2.0.0',
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
added: {
|
||||
dependencyType: 'optional',
|
||||
name: 'lala',
|
||||
version: '1.1.0',
|
||||
id: 'registry.npmjs.org/lala/1.1.0',
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
removed: {
|
||||
dependencyType: 'optional',
|
||||
name: 'is-positive',
|
||||
},
|
||||
})
|
||||
rootLogger.debug({
|
||||
linked: {
|
||||
dependencyType: 'optional',
|
||||
from: '/src/is-linked',
|
||||
name: 'is-linked',
|
||||
to: '/src/project/node_modules'
|
||||
},
|
||||
})
|
||||
rootLogger.info({
|
||||
added: {
|
||||
dependencyType: 'prod',
|
||||
name: 'winston',
|
||||
realName: 'winst0n',
|
||||
version: '1.0.0',
|
||||
latest: '1.0.0',
|
||||
id: 'registry.npmjs.org/winst0n/2.0.0',
|
||||
},
|
||||
})
|
||||
packageJsonLogger.debug({
|
||||
updated: {
|
||||
dependencies: {
|
||||
'is-negative': '^1.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'is-13': '^1.0.0',
|
||||
},
|
||||
}
|
||||
})
|
||||
summaryLogger.info()
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.skip(1).take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
${WARN} ${DEPRECATED} bar@2.0.0: This package was deprecated because bla bla bla
|
||||
|
||||
${h1('dependencies:')}
|
||||
${ADD} bar ${versionColor('2.0.0')} ${DEPRECATED}
|
||||
${SUB} foo ${versionColor('0.1.0')}
|
||||
${ADD} foo ${versionColor('1.0.0')} ${versionColor('(2.0.0 is available)')}
|
||||
${SUB} is-13 ${versionColor('^1.0.0')}
|
||||
${ADD} is-negative ${versionColor('^1.0.0')}
|
||||
${ADD} winston <- winst0n ${versionColor('1.0.0')}
|
||||
|
||||
${h1('optionalDependencies:')}
|
||||
${LINKED} is-linked ${chalk.magentaBright('linked from')} ${chalk.grey('/src/is-linked')}
|
||||
${SUB} is-positive
|
||||
${ADD} lala ${versionColor('1.1.0')}
|
||||
|
||||
${h1('devDependencies:')}
|
||||
${ADD} is-13 ${versionColor('^1.0.0')}
|
||||
${SUB} is-negative ${versionColor('^1.0.0')}
|
||||
${ADD} qar ${versionColor('2.0.0')}
|
||||
` + '\n')
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('groups lifecycle output', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
script: 'node foo',
|
||||
stage: 'preinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo',
|
||||
stage: 'preinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
script: 'node foo',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo I',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/bar/1.0.0',
|
||||
script: 'node bar',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/bar/1.0.0',
|
||||
line: 'bar I',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo II',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo III',
|
||||
stage: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/qar/1.0.0',
|
||||
script: 'node qar',
|
||||
stage: 'install',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/qar/1.0.0',
|
||||
exitCode: 0,
|
||||
stage: 'install',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
depPath: 'registry.npmjs.org/foo/1.0.0',
|
||||
exitCode: 0,
|
||||
stage: 'postinstall',
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.skip(9).take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, EOL + stripIndents`
|
||||
registry.npmjs.org/foo/1.0.0 | ${PREINSTALL}$ node foo
|
||||
registry.npmjs.org/foo/1.0.0 | ${PREINSTALL}: foo
|
||||
|
||||
registry.npmjs.org/foo/1.0.0 | ${POSTINSTALL}$ node foo
|
||||
registry.npmjs.org/foo/1.0.0 | ${POSTINSTALL}: foo I
|
||||
registry.npmjs.org/foo/1.0.0 | ${POSTINSTALL}: foo II
|
||||
registry.npmjs.org/foo/1.0.0 | ${POSTINSTALL}: foo III
|
||||
|
||||
registry.npmjs.org/bar/1.0.0 | ${POSTINSTALL}$ node bar
|
||||
registry.npmjs.org/bar/1.0.0 | ${POSTINSTALL}: bar I
|
||||
|
||||
registry.npmjs.org/qar/1.0.0 | ${INSTALL}$ node qar
|
||||
registry.npmjs.org/qar/1.0.0 | ${INSTALL}: done
|
||||
`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
// Many libs use stderr for logging, so showing all stderr adds not much value
|
||||
test['skip']('prints lifecycle progress', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
lifecycleLogger.debug({
|
||||
pkgId: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo I',
|
||||
script: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
pkgId: 'registry.npmjs.org/bar/1.0.0',
|
||||
line: 'bar I',
|
||||
script: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.error({
|
||||
pkgId: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo II',
|
||||
script: 'postinstall',
|
||||
})
|
||||
lifecycleLogger.debug({
|
||||
pkgId: 'registry.npmjs.org/foo/1.0.0',
|
||||
line: 'foo III',
|
||||
script: 'postinstall',
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
const childOutputColor = chalk.grey
|
||||
const childOutputError = chalk.red
|
||||
|
||||
output$.skip(3).take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Running ${POSTINSTALL} for ${hlPkgId('registry.npmjs.org/foo/1.0.0')}: ${childOutputColor('foo I')}
|
||||
Running ${POSTINSTALL} for ${hlPkgId('registry.npmjs.org/foo/1.0.0')}! ${childOutputError('foo II')}
|
||||
Running ${POSTINSTALL} for ${hlPkgId('registry.npmjs.org/foo/1.0.0')}: ${childOutputColor('foo III')}
|
||||
Running ${POSTINSTALL} for ${hlPkgId('registry.npmjs.org/bar/1.0.0')}: ${childOutputColor('bar I')}
|
||||
`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints generic error', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
const err = new Error('some error')
|
||||
logger.error(err)
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
${ERROR} ${chalk.red('some error')}
|
||||
${new StackTracey(err.stack).pretty}
|
||||
`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints generic error when recursive install fails', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive'})
|
||||
|
||||
const err = new Error('some error')
|
||||
err['prefix'] = '/home/src/'
|
||||
logger.error(err, err)
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
/home/src/:
|
||||
${ERROR} ${chalk.red('some error')}
|
||||
${new StackTracey(err.stack).pretty}
|
||||
`)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints info', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
logger.info('info message')
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, 'info message')
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints progress of big files download', async t => {
|
||||
t.plan(6)
|
||||
|
||||
let output$ = toOutput$(createStreamParser(), {cmd: 'install', throttleProgress: 0})
|
||||
.map(normalizeNewline) as most.Stream<string>
|
||||
const stream$: most.Stream<string>[] = []
|
||||
|
||||
const pkgId1 = 'registry.npmjs.org/foo/1.0.0'
|
||||
const pkgId2 = 'registry.npmjs.org/bar/2.0.0'
|
||||
const pkgId3 = 'registry.npmjs.org/qar/3.0.0'
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, `Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}`))
|
||||
)
|
||||
|
||||
output$ = output$.skip(1)
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, stripIndents`
|
||||
Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}
|
||||
Downloading ${hlPkgId(pkgId1)}: ${hlValue('0 B')}/${hlValue('10.5 MB')}
|
||||
`))
|
||||
)
|
||||
|
||||
output$ = output$.skip(1)
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, stripIndents`
|
||||
Resolving: total ${hlValue('1')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}
|
||||
Downloading ${hlPkgId(pkgId1)}: ${hlValue('5.77 MB')}/${hlValue('10.5 MB')}
|
||||
`))
|
||||
)
|
||||
|
||||
output$ = output$.skip(2)
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, stripIndents`
|
||||
Resolving: total ${hlValue('2')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}
|
||||
Downloading ${hlPkgId(pkgId1)}: ${hlValue('7.34 MB')}/${hlValue('10.5 MB')}
|
||||
`, 'downloading of small package not reported'))
|
||||
)
|
||||
|
||||
output$ = output$.skip(3)
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, stripIndents`
|
||||
Resolving: total ${hlValue('3')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}
|
||||
Downloading ${hlPkgId(pkgId1)}: ${hlValue('7.34 MB')}/${hlValue('10.5 MB')}
|
||||
Downloading ${hlPkgId(pkgId3)}: ${hlValue('19.9 MB')}/${hlValue('21 MB')}
|
||||
`))
|
||||
)
|
||||
|
||||
output$ = output$.skip(1)
|
||||
|
||||
stream$.push(
|
||||
output$.take(1)
|
||||
.tap(output => t.equal(output, stripIndents`
|
||||
Downloading ${hlPkgId(pkgId1)}: ${hlValue('10.5 MB')}/${hlValue('10.5 MB')}, done
|
||||
Resolving: total ${hlValue('3')}, reused ${hlValue('0')}, downloaded ${hlValue('0')}
|
||||
Downloading ${hlPkgId(pkgId3)}: ${hlValue('19.9 MB')}/${hlValue('21 MB')}
|
||||
`))
|
||||
)
|
||||
|
||||
most.mergeArray(stream$)
|
||||
.subscribe({
|
||||
next: () => undefined,
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId: pkgId1,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_started',
|
||||
pkgId: pkgId1,
|
||||
size: 1024 * 1024 * 10, // 10 MB
|
||||
attempt: 1,
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_progress',
|
||||
pkgId: pkgId1,
|
||||
downloaded: 1024 * 1024 * 5.5, // 5.5 MB
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId: pkgId2,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_started',
|
||||
pkgId: pkgId1,
|
||||
size: 10, // 10 B
|
||||
attempt: 1,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_progress',
|
||||
pkgId: pkgId1,
|
||||
downloaded: 1024 * 1024 * 7,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'resolving_content',
|
||||
pkgId: pkgId3,
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_started',
|
||||
pkgId: pkgId3,
|
||||
size: 1024 * 1024 * 20, // 20 MB
|
||||
attempt: 1,
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_progress',
|
||||
pkgId: pkgId3,
|
||||
downloaded: 1024 * 1024 * 19, // 19 MB
|
||||
})
|
||||
|
||||
progressLogger.debug({
|
||||
status: 'fetching_progress',
|
||||
pkgId: pkgId1,
|
||||
downloaded: 1024 * 1024 * 10, // 10 MB
|
||||
})
|
||||
})
|
||||
|
||||
test('prints added/removed stats during installation', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
statsLogger.debug({ added: 5 })
|
||||
statsLogger.debug({ removed: 1 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+5')} ${chalk.red('-1')}
|
||||
${ADD + ADD + ADD + ADD + ADD + SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints added/removed stats during installation when 0 removed', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
statsLogger.debug({ added: 2 })
|
||||
statsLogger.debug({ removed: 0 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+2')}
|
||||
${ADD + ADD}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints only the added stats if nothing was removed', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
statsLogger.debug({ removed: 0 })
|
||||
statsLogger.debug({ added: 1 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+1')}
|
||||
${ADD}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints only the removed stats if nothing was added', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
statsLogger.debug({ removed: 1 })
|
||||
statsLogger.debug({ added: 0 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.red('-1')}
|
||||
${SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints only the added stats if nothing was removed and a lot added', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', width: 20})
|
||||
|
||||
statsLogger.debug({ removed: 0 })
|
||||
statsLogger.debug({ added: 100 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+100')}
|
||||
${R.repeat(ADD, 20).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints only the removed stats if nothing was added and a lot removed', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', width: 20})
|
||||
|
||||
statsLogger.debug({ removed: 100 })
|
||||
statsLogger.debug({ added: 0 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.red('-100')}
|
||||
${R.repeat(SUB, 20).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints at least one remove sign when removed !== 0', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', width: 20})
|
||||
|
||||
statsLogger.debug({ removed: 1 })
|
||||
statsLogger.debug({ added: 100 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+100')} ${chalk.red('-1')}
|
||||
${R.repeat(ADD, 19).join('') + SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints at least one add sign when added !== 0', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', width: 20})
|
||||
|
||||
statsLogger.debug({ removed: 100 })
|
||||
statsLogger.debug({ added: 1 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.green('+1')} ${chalk.red('-100')}
|
||||
${ADD + R.repeat(SUB, 19).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints just removed during uninstallation', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'uninstall'})
|
||||
|
||||
statsLogger.debug({ removed: 4 })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
Packages: ${chalk.red('-4')}
|
||||
${SUB + SUB + SUB + SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints added/removed stats during recursive installation', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', cwd: '/home/jane/repo'})
|
||||
|
||||
statsLogger.debug({ removed: 1, prefix: '/home/jane/repo' })
|
||||
statsLogger.debug({ added: 0, prefix: '/home/jane/repo' })
|
||||
statsLogger.debug({ removed: 0, prefix: '/home/jane/repo/pkg-5' })
|
||||
statsLogger.debug({ added: 0, prefix: '/home/jane/repo/pkg-5' })
|
||||
statsLogger.debug({ added: 2, prefix: '/home/jane/repo/dir/pkg-2' })
|
||||
statsLogger.debug({ added: 5, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ removed: 1, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ removed: 0, prefix: '/home/jane/repo/dir/pkg-2' })
|
||||
statsLogger.debug({ removed: 0, prefix: '/home/jane/repo/loooooooooooooooooooooooooooooooooong/pkg-3' })
|
||||
statsLogger.debug({ added: 1, prefix: '/home/jane/repo/loooooooooooooooooooooooooooooooooong/pkg-3' })
|
||||
statsLogger.debug({ removed: 1, prefix: '/home/jane/repo/loooooooooooooooooooooooooooooooooong-pkg-4' })
|
||||
statsLogger.debug({ added: 0, prefix: '/home/jane/repo/loooooooooooooooooooooooooooooooooong-pkg-4' })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.skip(4).take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
. | ${chalk.red('-1')} ${SUB}
|
||||
pkg-1 | ${chalk.green('+5')} ${chalk.red('-1')} ${ADD + SUB}
|
||||
dir/pkg-2 | ${chalk.green('+2')} ${ADD}
|
||||
.../pkg-3 | ${chalk.green('+1')} ${ADD}
|
||||
...ooooooooooooooooooooooooooooong-pkg-4 | ${chalk.red('-1')} ${SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('recursive installation: prints only the added stats if nothing was removed and a lot added', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', width: 60, cwd: '/home/jane/repo'})
|
||||
|
||||
statsLogger.debug({ removed: 0, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ added: 190, prefix: '/home/jane/repo/pkg-1' })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
pkg-1 | ${chalk.green('+190')} ${R.repeat(ADD, 12).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('recursive installation: prints only the removed stats if nothing was added and a lot removed', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', width: 60, cwd: '/home/jane/repo'})
|
||||
|
||||
statsLogger.debug({ removed: 190, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ added: 0, prefix: '/home/jane/repo/pkg-1' })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
pkg-1 | ${chalk.red('-190')} ${R.repeat(SUB, 12).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('recursive installation: prints at least one remove sign when removed !== 0', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', width: 62, cwd: '/home/jane/repo'})
|
||||
|
||||
statsLogger.debug({ removed: 1, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ added: 100, prefix: '/home/jane/repo/pkg-1' })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
pkg-1 | ${chalk.green('+100')} ${chalk.red('-1')} ${R.repeat(ADD, 8).join('') + SUB}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('recursive installation: prints at least one add sign when added !== 0', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', width: 62, cwd: '/home/jane/repo'})
|
||||
|
||||
statsLogger.debug({ removed: 100, prefix: '/home/jane/repo/pkg-1' })
|
||||
statsLogger.debug({ added: 1, prefix: '/home/jane/repo/pkg-1' })
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
pkg-1 | ${chalk.green('+1')} ${chalk.red('-100')} ${ADD + R.repeat(SUB, 8).join('')}`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('install: print hook message', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install', cwd: '/home/jane/repo'})
|
||||
|
||||
hookLogger.debug({
|
||||
from: '/home/jane/repo/pnpmfile.js',
|
||||
prefix: '/home/jane/repo',
|
||||
hook: 'readPackage',
|
||||
message: 'foo',
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
${chalk.magentaBright('readPackage')}: foo`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('recursive: print hook message', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'recursive', cwd: '/home/jane/repo'})
|
||||
|
||||
hookLogger.debug({
|
||||
from: '/home/jane/repo/pnpmfile.js',
|
||||
prefix: '/home/jane/repo/pkg-1',
|
||||
hook: 'readPackage',
|
||||
message: 'foo',
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).map(normalizeNewline).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, stripIndents`
|
||||
pkg-1 | ${chalk.magentaBright('readPackage')}: foo`
|
||||
)
|
||||
},
|
||||
complete: () => t.end(),
|
||||
error: t.end,
|
||||
})
|
||||
})
|
||||
|
||||
test('prints skipped optional dependency info message', t => {
|
||||
const output$ = toOutput$(createStreamParser(), {cmd: 'install'})
|
||||
|
||||
const pkgId = 'registry.npmjs.org/foo/1.0.0'
|
||||
|
||||
skippedOptionalDependencyLogger.debug({
|
||||
package: {
|
||||
id: pkgId,
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
},
|
||||
parents: [],
|
||||
reason: 'unsupported_platform',
|
||||
})
|
||||
|
||||
t.plan(1)
|
||||
|
||||
output$.take(1).subscribe({
|
||||
next: output => {
|
||||
t.equal(output, `info: ${pkgId} is an optional dependency and failed compatibility check. Excluding it from installation.`)
|
||||
},
|
||||
error: t.end,
|
||||
complete: () => t.end(),
|
||||
})
|
||||
})
|
||||
24
packages/default-reporter/tsconfig.json
Normal file
24
packages/default-reporter/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictNullChecks": true,
|
||||
"target": "es6",
|
||||
"outDir": "lib",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"typings/**/*.d.ts"
|
||||
],
|
||||
"atom": {
|
||||
"rewriteTsconfig": true
|
||||
}
|
||||
}
|
||||
45
packages/default-reporter/tslint.json
Normal file
45
packages/default-reporter/tslint.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"curly": false,
|
||||
"eofline": false,
|
||||
"align": [true, "parameters"],
|
||||
"class-name": true,
|
||||
"indent": [true, "spaces"],
|
||||
"max-line-length": false,
|
||||
"no-any": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-var-keyword": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-requires": true,
|
||||
"no-require-imports": false,
|
||||
"no-string-literal": false,
|
||||
"space-before-function-paren": [true, "always"],
|
||||
"interface-name": [true, "never-prefix"],
|
||||
"no-console": false,
|
||||
"one-line": [true,
|
||||
"check-else",
|
||||
"check-whitespace",
|
||||
"check-open-brace"],
|
||||
"quotemark": [true,
|
||||
"single",
|
||||
"avoid-escape"],
|
||||
"semicolon": false,
|
||||
"typedef-whitespace": [true, {
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}],
|
||||
"whitespace": [true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"]
|
||||
}
|
||||
}
|
||||
1
packages/default-reporter/typings/index.d.ts
vendored
Normal file
1
packages/default-reporter/typings/index.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference path="local.d.ts" />
|
||||
54
packages/default-reporter/typings/local.d.ts
vendored
Normal file
54
packages/default-reporter/typings/local.d.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
declare module 'ansi-diff' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'cli-cursor' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'ndjson' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'normalize-newline' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'pretty-bytes' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'stacktracey' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'zen-push' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'right-pad' {
|
||||
const anything: any;
|
||||
export = anything;
|
||||
}
|
||||
|
||||
declare module 'string-length' {
|
||||
function stringLength (s: string): number;
|
||||
export = stringLength;
|
||||
}
|
||||
|
||||
declare module 'normalize-path' {
|
||||
function normalize (path: string): string;
|
||||
export = normalize;
|
||||
}
|
||||
|
||||
declare module 'string.prototype.padstart' {
|
||||
function padStart (s: string, targetLength: number, padString?: string): string;
|
||||
export = padStart;
|
||||
}
|
||||
Reference in New Issue
Block a user