diff options
author | Nathan Fritz <fritzy@github.com> | 2021-11-16 21:27:55 +0300 |
---|---|---|
committer | Nathan Fritz <fritzy@github.com> | 2021-11-18 22:43:26 +0300 |
commit | 44e39ce7042a162f53a708ee4660e84638a66f6b (patch) | |
tree | 79af3760604b7ce37e3c733a5aeb3726531ae9c6 /workspaces/libnpmdiff | |
parent | ea2906ca32bda348b2833458729dc52081d64cf2 (diff) |
separate github actions for workspaces
PR-URL: https://github.com/npm/cli/pull/4050
Credit: @fritzy
Close: #4050
Reviewed-by: @wraithgar
PR-URL: https://github.com/npm/cli/pull/4050
Credit: @fritzy
Close: #4050
Reviewed-by: @wraithgar
Diffstat (limited to 'workspaces/libnpmdiff')
22 files changed, 2003 insertions, 0 deletions
diff --git a/workspaces/libnpmdiff/.eslintrc.json b/workspaces/libnpmdiff/.eslintrc.json new file mode 100644 index 000000000..b39431d2c --- /dev/null +++ b/workspaces/libnpmdiff/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["@npmcli"] +} diff --git a/workspaces/libnpmdiff/.gitignore b/workspaces/libnpmdiff/.gitignore new file mode 100644 index 000000000..0aba557bf --- /dev/null +++ b/workspaces/libnpmdiff/.gitignore @@ -0,0 +1,99 @@ +# Logs +logs +*.log +npm-debug.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# gatsby files +.cache/ +public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# Editors +Session.vim diff --git a/workspaces/libnpmdiff/CHANGELOG.md b/workspaces/libnpmdiff/CHANGELOG.md new file mode 100644 index 000000000..b93b15b7b --- /dev/null +++ b/workspaces/libnpmdiff/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +## 2.0.3 + +- fix name of options sent by the npm cli + +## 2.0.2 + +- fix matching basename file filter + +## 2.0.1 + +- fix for tarballs not listing folder names + +## 2.0.0 + +- API rewrite: + - normalized all options + - specs to compare are now an array +- fix context=0 +- added support to filtering by folder names + +## 1.0.1 + +- fixed nameOnly option + +## 1.0.0 + +- Initial release + diff --git a/workspaces/libnpmdiff/LICENSE b/workspaces/libnpmdiff/LICENSE new file mode 100644 index 000000000..d3a1cdfd2 --- /dev/null +++ b/workspaces/libnpmdiff/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) GitHub Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/workspaces/libnpmdiff/README.md b/workspaces/libnpmdiff/README.md new file mode 100644 index 000000000..6c60f714b --- /dev/null +++ b/workspaces/libnpmdiff/README.md @@ -0,0 +1,98 @@ +# libnpmdiff + +[![npm version](https://img.shields.io/npm/v/libnpmdiff.svg)](https://npm.im/libnpmdiff) +[![license](https://img.shields.io/npm/l/libnpmdiff.svg)](https://npm.im/libnpmdiff) +[![GitHub Actions](https://github.com/npm/libnpmdiff/workflows/node-ci/badge.svg)](https://github.com/npm/libnpmdiff/actions?query=workflow%3Anode-ci) +[![Coverage Status](https://coveralls.io/repos/github/npm/libnpmdiff/badge.svg?branch=main)](https://coveralls.io/github/npm/libnpmdiff?branch=main) + +The registry diff lib. + +## Table of Contents + +* [Example](#example) +* [Install](#install) +* [Contributing](#contributing) +* [API](#api) +* [LICENSE](#license) + +## Example + +```js +const libdiff = require('libnpmdiff') + +const patch = await libdiff([ + 'abbrev@1.1.0', + 'abbrev@1.1.1' +]) +console.log( + patch +) +``` + +Returns: + +```patch +diff --git a/package.json b/package.json +index v1.1.0..v1.1.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "abbrev", +- "version": "1.1.0", ++ "version": "1.1.1", + "description": "Like ruby's abbrev module, but in js", + "author": "Isaac Z. Schlueter <i@izs.me>", + "main": "abbrev.js", + +``` + +## Install + +`$ npm install libnpmdiff` + +### Contributing + +The npm team enthusiastically welcomes contributions and project participation! +There's a bunch of things you can do if you want to contribute! The +[Contributor Guide](https://github.com/npm/cli/blob/latest/CONTRIBUTING.md) +outlines the process for community interaction and contribution. Please don't +hesitate to jump in if you'd like to, or even ask us questions if something +isn't clear. + +All participants and maintainers in this project are expected to follow the +[npm Code of Conduct](https://docs.npmjs.com/policies/conduct), and just +generally be excellent to each other. + +Please refer to the [Changelog](CHANGELOG.md) for project history details, too. + +Happy hacking! + +### API + +#### `> libnpmdif([ a, b ], [opts]) -> Promise<String>` + +Fetches the registry tarballs and compare files between a spec `a` and spec `b`. **npm** spec types are usually described in `<pkg-name>@<version>` form but multiple other types are alsos supported, for more info on valid specs take a look at [`npm-package-arg`](https://github.com/npm/npm-package-arg). + +**Options**: + +- `color <Boolean>`: Should add ANSI colors to string output? Defaults to `false`. +- `tagVersionPrefix <Sring>`: What prefix should be used to define version numbers. Defaults to `v` +- `diffUnified <Number>`: How many lines of code to print before/after each diff. Defaults to `3`. +- `diffFiles <Array<String>>`: If set only prints patches for the files listed in this array (also accepts globs). Defaults to `undefined`. +- `diffIgnoreAllSpace <Boolean>`: Whether or not should ignore changes in whitespace (very useful to avoid indentation changes extra diff lines). Defaults to `false`. +- `diffNameOnly <Boolean>`: Prints only file names and no patch diffs. Defaults to `false`. +- `diffNoPrefix <Boolean>`: If true then skips printing any prefixes in filenames. Defaults to `false`. +- `diffSrcPrefix <String>`: Prefix to be used in the filenames from `a`. Defaults to `a/`. +- `diffDstPrefix <String>`: Prefix to be used in the filenames from `b`. Defaults to `b/`. +- `diffText <Boolean>`: Should treat all files as text and try to print diff for binary files. Defaults to `false`. +- ...`cache`, `registry`, `where` and other common options accepted by [pacote](https://github.com/npm/pacote#options) + +Returns a `Promise` that fullfils with a `String` containing the resulting patch diffs. + +Throws an error if either `a` or `b` are missing or if trying to diff more than two specs. + +## LICENSE + +[ISC](./LICENSE) + diff --git a/workspaces/libnpmdiff/index.js b/workspaces/libnpmdiff/index.js new file mode 100644 index 000000000..6e68e6c4c --- /dev/null +++ b/workspaces/libnpmdiff/index.js @@ -0,0 +1,62 @@ +const pacote = require('pacote') + +const formatDiff = require('./lib/format-diff.js') +const getTarball = require('./lib/tarball.js') +const untar = require('./lib/untar.js') + +// TODO: we test this condition in the diff command +// so this error probably doesnt need to be here. Or +// if it does we should figure out a standard code +// so we can catch it in the cli and display it consistently +const argsError = () => + Object.assign( + new TypeError('libnpmdiff needs two arguments to compare'), + { code: 'EDIFFARGS' } + ) +const diff = async (specs, opts = {}) => { + if (specs.length !== 2) { + throw argsError() + } + + const [ + aManifest, + bManifest, + ] = + await Promise.all(specs.map(spec => pacote.manifest(spec, opts))) + + const versions = { + a: aManifest.version, + b: bManifest.version, + } + + // fetches tarball using pacote + const [a, b] = await Promise.all([ + getTarball(aManifest, opts), + getTarball(bManifest, opts), + ]) + + // read all files + // populates `files` and `refs` + const { + files, + refs, + } = await untar([ + { + prefix: 'a/', + item: a, + }, + { + prefix: 'b/', + item: b, + }, + ], opts) + + return formatDiff({ + files, + opts, + refs, + versions, + }) +} + +module.exports = diff diff --git a/workspaces/libnpmdiff/lib/format-diff.js b/workspaces/libnpmdiff/lib/format-diff.js new file mode 100644 index 000000000..211386cb5 --- /dev/null +++ b/workspaces/libnpmdiff/lib/format-diff.js @@ -0,0 +1,96 @@ +const EOL = '\n' + +const colorizeDiff = require('@npmcli/disparity-colors') +const jsDiff = require('diff') + +const shouldPrintPatch = require('./should-print-patch.js') + +const formatDiff = ({ files, opts = {}, refs, versions }) => { + let res = '' + const srcPrefix = opts.diffNoPrefix ? '' : opts.diffSrcPrefix || 'a/' + const dstPrefix = opts.diffNoPrefix ? '' : opts.diffDstPrefix || 'b/' + + for (const filename of files.values()) { + const names = { + a: `${srcPrefix}${filename}`, + b: `${dstPrefix}${filename}`, + } + + let fileMode = '' + const filenames = { + a: refs.get(`a/${filename}`), + b: refs.get(`b/${filename}`), + } + const contents = { + a: filenames.a && filenames.a.content, + b: filenames.b && filenames.b.content, + } + const modes = { + a: filenames.a && filenames.a.mode, + b: filenames.b && filenames.b.mode, + } + + if (contents.a === contents.b && modes.a === modes.b) { + continue + } + + if (opts.diffNameOnly) { + res += `${filename}${EOL}` + continue + } + + let patch = '' + let headerLength = 0 + const header = str => { + headerLength++ + patch += `${str}${EOL}` + } + + // manually build a git diff-compatible header + header(`diff --git ${names.a} ${names.b}`) + if (modes.a === modes.b) { + fileMode = filenames.a.mode + } else { + if (modes.a && !modes.b) { + header(`deleted file mode ${modes.a}`) + } else if (!modes.a && modes.b) { + header(`new file mode ${modes.b}`) + } else { + header(`old mode ${modes.a}`) + header(`new mode ${modes.b}`) + } + } + /* eslint-disable-next-line max-len */ + header(`index ${opts.tagVersionPrefix || 'v'}${versions.a}..${opts.tagVersionPrefix || 'v'}${versions.b} ${fileMode}`) + + if (shouldPrintPatch(filename)) { + patch += jsDiff.createTwoFilesPatch( + names.a, + names.b, + contents.a || '', + contents.b || '', + '', + '', + { + context: opts.diffUnified === 0 ? 0 : opts.diffUnified || 3, + ignoreWhitespace: opts.diffIgnoreAllSpace, + } + ).replace( + '===================================================================\n', + '' + ).replace(/\t\n/g, '\n') // strip trailing tabs + headerLength += 2 + } else { + header(`--- ${names.a}`) + header(`+++ ${names.b}`) + } + + res += (opts.color + ? colorizeDiff(patch, { headerLength }) + : patch) + } + + return res.trim() +} + +module.exports = formatDiff diff --git a/workspaces/libnpmdiff/lib/should-print-patch.js b/workspaces/libnpmdiff/lib/should-print-patch.js new file mode 100644 index 000000000..a95481140 --- /dev/null +++ b/workspaces/libnpmdiff/lib/should-print-patch.js @@ -0,0 +1,22 @@ +const { basename, extname } = require('path') + +const binaryExtensions = require('binary-extensions') + +// we should try to print patches as long as the +// extension is not identified as binary files +const shouldPrintPatch = (path, opts = {}) => { + if (opts.diffText) { + return true + } + + const filename = basename(path) + const extension = ( + filename.startsWith('.') + ? filename + : extname(filename) + ).substr(1) + + return !binaryExtensions.includes(extension) +} + +module.exports = shouldPrintPatch diff --git a/workspaces/libnpmdiff/lib/tarball.js b/workspaces/libnpmdiff/lib/tarball.js new file mode 100644 index 000000000..4d01d69c9 --- /dev/null +++ b/workspaces/libnpmdiff/lib/tarball.js @@ -0,0 +1,34 @@ +const { relative } = require('path') + +const npa = require('npm-package-arg') +const pkgContents = require('@npmcli/installed-package-contents') +const pacote = require('pacote') +const { tarCreateOptions } = pacote.DirFetcher +const tar = require('tar') + +// returns a simplified tarball when reading files from node_modules folder, +// thus avoiding running the prepare scripts and the extra logic from packlist +const nodeModulesTarball = (manifest, opts) => + pkgContents({ path: manifest._resolved, depth: 1 }) + .then(files => + files.map(file => relative(manifest._resolved, file)) + ) + .then(files => + tar.c(tarCreateOptions(manifest), files).concat() + ) + +const tarball = (manifest, opts) => { + const resolved = manifest._resolved + const where = opts.where || process.cwd() + + const fromNodeModules = npa(resolved).type === 'directory' + && /node_modules[\\/](@[^\\/]+\/)?[^\\/]+[\\/]?$/.test(relative(where, resolved)) + + if (fromNodeModules) { + return nodeModulesTarball(manifest, opts) + } + + return pacote.tarball(manifest._resolved, opts) +} + +module.exports = tarball diff --git a/workspaces/libnpmdiff/lib/untar.js b/workspaces/libnpmdiff/lib/untar.js new file mode 100644 index 000000000..16b69ab8f --- /dev/null +++ b/workspaces/libnpmdiff/lib/untar.js @@ -0,0 +1,96 @@ +const tar = require('tar') +const minimatch = require('minimatch') + +const normalizeMatch = str => str + .replace(/\\+/g, '/') + .replace(/^\.\/|^\./, '') + +// files and refs are mutating params +// filterFiles, item, prefix and opts are read-only options +const untar = ({ files, refs }, { filterFiles, item, prefix }) => { + tar.list({ + filter: (path, entry) => { + const fileMatch = () => + (!filterFiles.length || + filterFiles.some(f => { + const pattern = normalizeMatch(f) + return minimatch( + normalizeMatch(path), + `{package/,}${pattern}`, + { matchBase: pattern.startsWith('*') } + ) + })) + + // expands usage of simple path filters, e.g: lib or src/ + const folderMatch = () => + filterFiles.some(f => + normalizeMatch(path).startsWith(normalizeMatch(f)) || + normalizeMatch(path).startsWith(`package/${normalizeMatch(f)}`)) + + if ( + entry.type === 'File' && + (fileMatch() || folderMatch()) + ) { + const key = path.replace(/^[^/]+\/?/, '') + files.add(key) + + // should skip reading file when using --name-only option + let content + try { + entry.setEncoding('utf8') + content = entry.concat() + } catch (e) { + /* istanbul ignore next */ + throw Object.assign( + new Error('failed to read files'), + { code: 'EDIFFUNTAR' } + ) + } + + refs.set(`${prefix}${key}`, { + content, + mode: `100${entry.mode.toString(8)}`, + }) + return true + } + }, + }) + .on('error', /* istanbul ignore next */ e => { + throw e + }) + .end(item) +} + +const readTarballs = async (tarballs, opts = {}) => { + const files = new Set() + const refs = new Map() + const arr = [].concat(tarballs) + + const filterFiles = opts.diffFiles || [] + + for (const i of arr) { + untar({ + files, + refs, + }, { + item: i.item, + prefix: i.prefix, + filterFiles, + }) + } + + // await to read all content from included files + const allRefs = [...refs.values()] + const contents = await Promise.all(allRefs.map(async ref => ref.content)) + + contents.forEach((content, index) => { + allRefs[index].content = content + }) + + return { + files, + refs, + } +} + +module.exports = readTarballs diff --git a/workspaces/libnpmdiff/package.json b/workspaces/libnpmdiff/package.json new file mode 100644 index 000000000..129d9b90c --- /dev/null +++ b/workspaces/libnpmdiff/package.json @@ -0,0 +1,62 @@ +{ + "name": "libnpmdiff", + "version": "2.0.4", + "description": "The registry diff", + "repository": "https://github.com/npm/libnpmdiff", + "files": [ + "index.js", + "lib" + ], + "engines": { + "node": ">=10" + }, + "keywords": [ + "npm", + "npmcli", + "libnpm", + "cli", + "diff" + ], + "author": "GitHub Inc.", + "contributors": [ + { + "name": "Ruy Adorno", + "url": "https://ruyadorno.com", + "twitter": "ruyadorno" + } + ], + "license": "ISC", + "scripts": { + "eslint": "eslint", + "lint": "npm run eslint -- index.js \"lib/**/*.js\" \"test/*.js\"", + "lintfix": "npm run lint -- --fix", + "test": "tap test/*.js", + "posttest": "npm run lint", + "snap": "tap test/*.js", + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags" + }, + "tap": { + "check-coverage": true + }, + "standard": { + "ignore": [ + "/tap-snapshots/" + ] + }, + "devDependencies": { + "eslint": "^8.1.0", + "tap": "^15.0.9" + }, + "dependencies": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.4", + "pacote": "^12.0.0", + "tar": "^6.1.0" + } +} diff --git a/workspaces/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs b/workspaces/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs new file mode 100644 index 000000000..f735d8925 --- /dev/null +++ b/workspaces/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs @@ -0,0 +1,152 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/format-diff.js TAP added file > should output expected added file diff result 1`] = ` +diff --git a/foo.js b/foo.js +new file mode 100755 +index v1.0.0..v2.0.0 +--- a/foo.js ++++ b/foo.js +@@ -0,0 +1,2 @@ ++"use strict" ++module.exports = "foo" +` + +exports[`test/format-diff.js TAP binary file > should output expected bin file diff result 1`] = ` +diff --git a/foo.jpg b/foo.jpg +index v1.0.0..v2.0.0 100644 +--- a/foo.jpg ++++ b/foo.jpg +` + +exports[`test/format-diff.js TAP changed file mode > should output expected changed file mode diff result 1`] = ` +diff --git a/foo.js b/foo.js +old mode 100644 +new mode 100755 +index v1.0.0..v2.0.0 +--- a/foo.js ++++ b/foo.js +` + +exports[`test/format-diff.js TAP colored output > should output expected colored diff result 1`] = ` +[33mdiff --git a/foo.js b/foo.js[39m +[33mindex v1.0.0..v2.0.0 100644[39m +[33m--- a/foo.js[39m +[33m+++ b/foo.js[39m +[35m@@ -1,2 +1,2 @@[39m + "use strict" +[31m-module.exports = "foo"[39m +[32m+module.exports = "foobar"[39m +` + +exports[`test/format-diff.js TAP diff options > should output expected diff result 1`] = ` +diff --git before/foo.js after/foo.js +index v1.0.0..v2.0.0 100644 +--- before/foo.js ++++ after/foo.js +@@ -4,4 +4,6 @@ + const c = "c" ++const d = "d" + module.exports = () => a+ + b+ +-c ++c+ ++d +` + +exports[`test/format-diff.js TAP diffUnified=0 > should output no context lines in output 1`] = ` +diff --git a/foo.js b/foo.js +index v1.0.0..v2.0.0 100644 +--- a/foo.js ++++ b/foo.js +@@ -3,2 +3,3 @@ +-const b = "b" +-const c = "c" ++ const b = "b" ++ const c = "c" ++ const d = "d" +@@ -7,1 +8,2 @@ +-c ++c+ ++d +` + +exports[`test/format-diff.js TAP format multiple files patch > should output expected result for multiple files 1`] = ` +diff --git a/foo.js b/foo.js +index v1.0.0..v1.1.1 100644 +--- a/foo.js ++++ b/foo.js +@@ -1,2 +1,2 @@ + "use strict" +-module.exports = "foo" ++module.exports = "foobar" +diff --git a/lib/utils.js b/lib/utils.js +index v1.0.0..v1.1.1 100644 +--- a/lib/utils.js ++++ b/lib/utils.js +@@ -1,3 +1,4 @@ + "use strict" + const bar = require("./bar.js") +-module.exports = () => bar ++module.exports = ++ () => bar + "util" +` + +exports[`test/format-diff.js TAP format removed file > should output expected removed file diff result 1`] = ` +diff --git a/foo.js b/foo.js +deleted file mode 100644 +index v1.0.0..v2.0.0 +--- a/foo.js ++++ b/foo.js +@@ -1,2 +0,0 @@ +-"use strict" +-module.exports = "foo" +/ No newline at end of file +` + +exports[`test/format-diff.js TAP format simple diff > should output expected diff result 1`] = ` +diff --git a/foo.js b/foo.js +index v1.0.0..v2.0.0 100644 +--- a/foo.js ++++ b/foo.js +@@ -1,2 +1,2 @@ + "use strict" +-module.exports = "foo" ++module.exports = "foobar" +` + +exports[`test/format-diff.js TAP noPrefix > should output result with no prefixes 1`] = ` +diff --git foo.js foo.js +index v1.0.0..v2.0.0 100644 +Index: foo.js +--- foo.js ++++ foo.js +@@ -1,2 +1,2 @@ + "use strict" +-module.exports = "foo" ++module.exports = "foobar" +` + +exports[`test/format-diff.js TAP nothing to diff > should output empty result 1`] = ` + +` + +exports[`test/format-diff.js TAP respect --tag-version-prefix option > should output expected diff result 1`] = ` +diff --git a/foo.js b/foo.js +index b1.0.0..b2.0.0 100644 +--- a/foo.js ++++ b/foo.js +@@ -1,2 +1,2 @@ + "use strict" +-module.exports = "foo" ++module.exports = "foobar" +` + +exports[`test/format-diff.js TAP using --name-only option > should output expected diff result 1`] = ` +foo.js +lib/utils.js +` diff --git a/workspaces/libnpmdiff/tap-snapshots/test/index.js.test.cjs b/workspaces/libnpmdiff/tap-snapshots/test/index.js.test.cjs new file mode 100644 index 000000000..21db3deac --- /dev/null +++ b/workspaces/libnpmdiff/tap-snapshots/test/index.js.test.cjs @@ -0,0 +1,115 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/index.js TAP compare two diff specs > should output expected diff 1`] = ` +diff --git a/index.js b/index.js +index v1.0.0..v2.0.0 100644 +--- a/index.js ++++ b/index.js +@@ -1,2 +1,2 @@ + module.exports = +- "a1" ++ "a2" +diff --git a/package.json b/package.json +index v1.0.0..v2.0.0 100644 +--- a/package.json ++++ b/package.json +@@ -1,4 +1,4 @@ + { + "name": "a", +- "version": "1.0.0" ++ "version": "2.0.0" + } +` + +exports[`test/index.js TAP folder in node_modules nested, absolute path > should output expected diff 1`] = ` +diff --git a/package.json b/package.json +index v2.0.0..v2.0.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "b", +- "version": "2.0.0", ++ "version": "2.0.1", + "scripts": { + "prepare": "node prepare.js" + } +diff --git a/prepare.js b/prepare.js +index v2.0.0..v2.0.1 100644 +--- a/prepare.js ++++ b/prepare.js +@@ -1,1 +0,0 @@ +-throw new Error("ERR") +/ No newline at end of file +` + +exports[`test/index.js TAP folder in node_modules nested, relative path > should output expected diff 1`] = ` +diff --git a/package.json b/package.json +index v2.0.0..v2.0.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "b", +- "version": "2.0.0", ++ "version": "2.0.1", + "scripts": { + "prepare": "node prepare.js" + } +diff --git a/prepare.js b/prepare.js +index v2.0.0..v2.0.1 100644 +--- a/prepare.js ++++ b/prepare.js +@@ -1,1 +0,0 @@ +-throw new Error("ERR") +/ No newline at end of file +` + +exports[`test/index.js TAP folder in node_modules top-level, absolute path > should output expected diff 1`] = ` +diff --git a/package.json b/package.json +index v1.0.0..v1.0.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "a", +- "version": "1.0.0", ++ "version": "1.0.1", + "scripts": { + "prepare": "node prepare.js" + } +diff --git a/prepare.js b/prepare.js +index v1.0.0..v1.0.1 100644 +--- a/prepare.js ++++ b/prepare.js +@@ -1,1 +0,0 @@ +-throw new Error("ERR") +/ No newline at end of file +` + +exports[`test/index.js TAP folder in node_modules top-level, relative path > should output expected diff 1`] = ` +diff --git a/package.json b/package.json +index v1.0.0..v1.0.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,6 +1,6 @@ + { + "name": "a", +- "version": "1.0.0", ++ "version": "1.0.1", + "scripts": { + "prepare": "node prepare.js" + } +diff --git a/prepare.js b/prepare.js +index v1.0.0..v1.0.1 100644 +--- a/prepare.js ++++ b/prepare.js +@@ -1,1 +0,0 @@ +-throw new Error("ERR") +/ No newline at end of file +` diff --git a/workspaces/libnpmdiff/tap-snapshots/test/untar.js.test.cjs b/workspaces/libnpmdiff/tap-snapshots/test/untar.js.test.cjs new file mode 100644 index 000000000..b1092feb6 --- /dev/null +++ b/workspaces/libnpmdiff/tap-snapshots/test/untar.js.test.cjs @@ -0,0 +1,134 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/untar.js TAP filter files > should return list of filenames 1`] = ` +LICENSE +README.md +` + +exports[`test/untar.js TAP filter files > should return map of filenames with valid contents 1`] = ` +a/LICENSE: true +a/README.md: true +` + +exports[`test/untar.js TAP filter files by exact filename > should return no filenames 1`] = ` + +` + +exports[`test/untar.js TAP filter files by exact filename > should return no filenames 2`] = ` + +` + +exports[`test/untar.js TAP filter files using glob expressions > should return list of filenames 1`] = ` +lib/index.js +lib/utils/b.js +package-lock.json +test/index.js +` + +exports[`test/untar.js TAP filter files using glob expressions > should return map of filenames with valid contents 1`] = ` +a/lib/index.js: true +a/lib/utils/b.js: true +a/package-lock.json: true +a/test/index.js: true +` + +exports[`test/untar.js TAP match files by end of filename > should return list of filenames 1`] = ` +lib/index.js +lib/utils/b.js +test/index.js +test/utils/b.js +` + +exports[`test/untar.js TAP match files by end of filename > should return map of filenames with valid contents 1`] = ` +a/lib/index.js: true +a/lib/utils/b.js: true +a/test/index.js: true +a/test/utils/b.js: true +` + +exports[`test/untar.js TAP match files by simple folder name > should return list of filenames 1`] = ` +lib/index.js +lib/utils/b.js +` + +exports[`test/untar.js TAP match files by simple folder name > should return map of filenames with valid contents 1`] = ` +a/lib/index.js: true +a/lib/utils/b.js: true +` + +exports[`test/untar.js TAP match files by simple folder name variation > should return list of filenames 1`] = ` +test/index.js +test/utils/b.js +` + +exports[`test/untar.js TAP match files by simple folder name variation > should return map of filenames with valid contents 1`] = ` +a/test/index.js: true +a/test/utils/b.js: true +` + +exports[`test/untar.js TAP untar package with folders > should have read contents 1`] = ` +module.exports = 'b' + +` + +exports[`test/untar.js TAP untar package with folders > should return list of filenames 1`] = ` +lib/index.js +lib/utils/b.js +package-lock.json +package.json +test/index.js +test/utils/b.js +` + +exports[`test/untar.js TAP untar package with folders > should return map of filenames to its contents 1`] = ` +a/lib/index.js: true +a/lib/utils/b.js: true +a/package-lock.json: true +a/package.json: true +a/test/index.js: true +a/test/utils/b.js: true +` + +exports[`test/untar.js TAP untar simple package > should have read contents 1`] = ` +The MIT License (MIT) + +Copyright (c) Ruy Adorno (ruyadorno.com) + +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. + +` + +exports[`test/untar.js TAP untar simple package > should return list of filenames 1`] = ` +LICENSE +index.js +package.json +README.md +` + +exports[`test/untar.js TAP untar simple package > should return map of filenames to its contents 1`] = ` +a/LICENSE: true +a/index.js: true +a/package.json: true +a/README.md: true +` diff --git a/workspaces/libnpmdiff/test/fixtures/archive.tgz b/workspaces/libnpmdiff/test/fixtures/archive.tgz Binary files differnew file mode 100644 index 000000000..843a61123 --- /dev/null +++ b/workspaces/libnpmdiff/test/fixtures/archive.tgz diff --git a/workspaces/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz b/workspaces/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz Binary files differnew file mode 100644 index 000000000..11bbb44c4 --- /dev/null +++ b/workspaces/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz diff --git a/workspaces/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz b/workspaces/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz Binary files differnew file mode 100644 index 000000000..8d442f4c1 --- /dev/null +++ b/workspaces/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz diff --git a/workspaces/libnpmdiff/test/format-diff.js b/workspaces/libnpmdiff/test/format-diff.js new file mode 100644 index 000000000..f2fc7c77d --- /dev/null +++ b/workspaces/libnpmdiff/test/format-diff.js @@ -0,0 +1,483 @@ +const t = require('tap') + +const formatDiff = require('../lib/format-diff.js') + +const normalizeWin = (str) => str + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +t.cleanSnapshot = (str) => normalizeWin(str) + +t.test('format simple diff', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected diff result' + ) + t.end() +}) + +t.test('nothing to diff', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '1.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output empty result' + ) + t.end() +}) + +t.test('format removed file', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected removed file diff result' + ) + t.end() +}) + +t.test('changed file mode', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100755', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected changed file mode diff result' + ) + t.end() +}) + +t.test('added file', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100755', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected added file diff result' + ) + t.end() +}) + +t.test('binary file', t => { + const files = new Set([ + 'foo.jpg', + ]) + const refs = new Map(Object.entries({ + 'a/foo.jpg': { + content: Buffer.from(''), + mode: '100644', + }, + 'b/foo.jpg': { + content: Buffer.from(''), + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected bin file diff result' + ) + t.end() +}) + +t.test('nothing to compare', t => { + const files = new Set([ + 'foo.jpg', + ]) + const refs = new Map(Object.entries({ + 'a/foo.jpg': {}, + 'b/foo.jpg': {}, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.equal( + formatDiff({ + files, + refs, + versions, + }), + '', + 'should have no output' + ) + t.end() +}) + +t.test('colored output', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + color: true, + }, + }), + 'should output expected colored diff result' + ) + t.end() +}) + +t.test('using --name-only option', t => { + const files = new Set([ + 'foo.js', + 'lib/bar.js', + 'lib/utils.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + 'a/lib/bar.js': { + content: '"use strict"\nmodule.exports = "bar"\n', + mode: '100644', + }, + 'b/lib/bar.js': { + content: '"use strict"\nmodule.exports = "bar"\n', + mode: '100644', + }, + 'a/lib/utils.js': { + content: '"use strict"\nconst bar = require("./bar.js")\n' + + 'module.exports = () => bar\n', + mode: '100644', + }, + 'b/lib/utils.js': { + content: '"use strict"\nconst bar = require("./bar.js")\n' + + 'module.exports =\n () => bar + "util"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + diffNameOnly: true, + }, + }), + 'should output expected diff result' + ) + t.end() +}) + +t.test('respect --tag-version-prefix option', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + tagVersionPrefix: 'b', + }, + }), + 'should output expected diff result' + ) + t.end() +}) + +t.test('diff options', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nconst a = "a"\nconst b = "b"\n' + + 'const c = "c"\nmodule.exports = () => a+\nb+\nc\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nconst a = "a"\n const b = "b"\n' + + ' const c = "c"\n const d = "d"\n' + + 'module.exports = () => a+\nb+\nc+\nd\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + diffUnified: 1, + diffIgnoreAllSpace: true, + diffSrcPrefix: 'before/', + diffDstPrefix: 'after/', + }, + }), + 'should output expected diff result' + ) + t.end() +}) + +t.test('diffUnified=0', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nconst a = "a"\nconst b = "b"\n' + + 'const c = "c"\nmodule.exports = () => a+\nb+\nc\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nconst a = "a"\n const b = "b"\n' + + ' const c = "c"\n const d = "d"\n' + + 'module.exports = () => a+\nb+\nc+\nd\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + diffUnified: 0, + }, + }), + 'should output no context lines in output' + ) + t.end() +}) + +t.test('noPrefix', t => { + const files = new Set([ + 'foo.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '2.0.0', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + opts: { + diffNoPrefix: true, + }, + }), + 'should output result with no prefixes' + ) + t.end() +}) + +t.test('format multiple files patch', t => { + const files = new Set([ + 'foo.js', + 'lib/bar.js', + 'lib/utils.js', + ]) + const refs = new Map(Object.entries({ + 'a/foo.js': { + content: '"use strict"\nmodule.exports = "foo"\n', + mode: '100644', + }, + 'b/foo.js': { + content: '"use strict"\nmodule.exports = "foobar"\n', + mode: '100644', + }, + 'a/lib/bar.js': { + content: '"use strict"\nmodule.exports = "bar"\n', + mode: '100644', + }, + 'b/lib/bar.js': { + content: '"use strict"\nmodule.exports = "bar"\n', + mode: '100644', + }, + 'a/lib/utils.js': { + content: '"use strict"\nconst bar = require("./bar.js")\n' + + 'module.exports = () => bar\n', + mode: '100644', + }, + 'b/lib/utils.js': { + content: '"use strict"\nconst bar = require("./bar.js")\n' + + 'module.exports =\n () => bar + "util"\n', + mode: '100644', + }, + })) + const versions = { + a: '1.0.0', + b: '1.1.1', + } + + t.matchSnapshot( + formatDiff({ + files, + refs, + versions, + }), + 'should output expected result for multiple files' + ) + t.end() +}) diff --git a/workspaces/libnpmdiff/test/index.js b/workspaces/libnpmdiff/test/index.js new file mode 100644 index 000000000..80b3daaa6 --- /dev/null +++ b/workspaces/libnpmdiff/test/index.js @@ -0,0 +1,147 @@ +const { resolve } = require('path') + +const t = require('tap') + +const diff = require('../index.js') + +const normalizePath = p => p + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +t.cleanSnapshot = (str) => normalizePath(str) + .replace(normalizePath(process.execPath), 'node') + +const json = (obj) => `${JSON.stringify(obj, null, 2)}\n` + +t.test('compare two diff specs', async t => { + const path = t.testdir({ + a1: { + 'package.json': json({ + name: 'a', + version: '1.0.0', + }), + 'index.js': 'module.exports =\n "a1"\n', + }, + a2: { + 'package.json': json({ + name: 'a', + version: '2.0.0', + }), + 'index.js': 'module.exports =\n "a2"\n', + }, + }) + + const a = `file:${resolve(path, 'a1')}` + const b = `file:${resolve(path, 'a2')}` + + t.resolveMatchSnapshot(diff([a, b], {}), 'should output expected diff') +}) + +t.test('using single arg', async t => { + await t.rejects( + diff(['abbrev@1.0.3']), + /libnpmdiff needs two arguments to compare/, + 'should throw EDIFFARGS error' + ) +}) + +t.test('too many args', async t => { + const args = ['abbrev@1.0.3', 'abbrev@1.0.4', 'abbrev@1.0.5'] + await t.rejects( + diff(args), + /libnpmdiff needs two arguments to compare/, + 'should output diff against cwd files' + ) +}) + +t.test('folder in node_modules', async t => { + const path = t.testdir({ + node_modules: { + a: { + 'package.json': json({ + name: 'a', + version: '1.0.0', + scripts: { + prepare: `${process.execPath} prepare.js`, + }, + }), + 'prepare.js': 'throw new Error("ERR")', + node_modules: { + b: { + 'package.json': json({ + name: 'b', + version: '2.0.0', + scripts: { + prepare: `${process.execPath} prepare.js`, + }, + }), + 'prepare.js': 'throw new Error("ERR")', + }, + }, + }, + }, + packages: { + a: { + 'package.json': json({ + name: 'a', + version: '1.0.1', + scripts: { + prepare: `${process.execPath} prepare.js`, + }, + }), + 'prepare.js': '', + }, + b: { + 'package.json': json({ + name: 'b', + version: '2.0.1', + scripts: { + prepare: `${process.execPath} prepare.js`, + }, + }), + 'prepare.js': '', + }, + }, + 'package.json': json({ + name: 'my-project', + version: '1.0.0', + }), + }) + + t.test('top-level, absolute path', async t => { + t.resolveMatchSnapshot(diff([ + `file:${resolve(path, 'node_modules/a')}`, + `file:${resolve(path, 'packages/a')}`, + ], { where: path }), 'should output expected diff') + }) + t.test('top-level, relative path', async t => { + const _cwd = process.cwd() + process.chdir(path) + t.teardown(() => { + process.chdir(_cwd) + }) + + t.resolveMatchSnapshot(diff([ + 'file:./node_modules/a', + 'file:./packages/a', + ], { where: path }), 'should output expected diff') + }) + t.test('nested, absolute path', async t => { + t.resolveMatchSnapshot(diff([ + `file:${resolve(path, 'node_modules/a/node_modules/b')}`, + `file:${resolve(path, 'packages/b')}`, + ], { where: path }), 'should output expected diff') + }) + t.test('nested, relative path', async t => { + const _cwd = process.cwd() + process.chdir(path) + t.teardown(() => { + process.chdir(_cwd) + }) + + t.resolveMatchSnapshot(diff([ + 'file:./node_modules/a/node_modules/b', + 'file:./packages/b', + ], { where: path }), 'should output expected diff') + }) +}) diff --git a/workspaces/libnpmdiff/test/should-print-patch.js b/workspaces/libnpmdiff/test/should-print-patch.js new file mode 100644 index 000000000..97b15787d --- /dev/null +++ b/workspaces/libnpmdiff/test/should-print-patch.js @@ -0,0 +1,28 @@ +const t = require('tap') +const shouldPrintPatch = require('../lib/should-print-patch.js') + +t.test('valid filenames', t => { + t.ok(shouldPrintPatch('LICENSE')) + t.ok(shouldPrintPatch('.gitignore')) + t.ok(shouldPrintPatch('foo.md')) + t.ok(shouldPrintPatch('./bar.txt')) + t.ok(shouldPrintPatch('/a/b/c/bar.html')) + t.end() +}) + +t.test('invalid filenames', t => { + t.notOk(shouldPrintPatch('foo.exe')) + t.notOk(shouldPrintPatch('./foo.jpg')) + t.notOk(shouldPrintPatch('/a/b/c/bar.bin')) + t.end() +}) + +t.test('using --text/-a option', t => { + const opts = { + diffText: true, + } + t.ok(shouldPrintPatch('foo.exe', opts)) + t.ok(shouldPrintPatch('./foo.jpg', opts)) + t.ok(shouldPrintPatch('/a/b/c/bar.bin', opts)) + t.end() +}) diff --git a/workspaces/libnpmdiff/test/tarball.js b/workspaces/libnpmdiff/test/tarball.js new file mode 100644 index 000000000..3a959be6e --- /dev/null +++ b/workspaces/libnpmdiff/test/tarball.js @@ -0,0 +1,96 @@ +const { resolve } = require('path') + +const t = require('tap') +const tar = require('tar') +const pacote = require('pacote') +pacote.tarball = () => { + throw new Error('Failed to detect node_modules tarball') +} + +const tarball = require('../lib/tarball.js') + +const json = (obj) => `${JSON.stringify(obj, null, 2)}\n` + +t.test('returns a tarball from node_modules', t => { + t.plan(2) + + const path = t.testdir({ + node_modules: { + a: { + 'package.json': json({ + name: 'a', + version: '1.0.0', + bin: { a: 'index.js' }, + }), + 'index.js': '', + }, + }, + }) + + const _cwd = process.cwd() + process.chdir(path) + t.teardown(() => { + process.chdir(_cwd) + }) + + tarball({ bin: { a: 'index.js' }, _resolved: resolve(path, 'node_modules/a') }, { where: path }) + .then(res => { + tar.list({ + filter: path => { + t.match( + path, + /package.json|index.js/, + 'should return tarball with expected files' + ) + }, + }) + .on('error', e => { + throw e + }) + .end(res) + }) +}) + +t.test('node_modules folder within a linked dir', async t => { + const path = t.testdir({ + node_modules: { + a: t.fixture('symlink', '../packages/a'), + }, + packages: { + a: { + node_modules: { + b: { + 'package.json': json({ + name: 'a', + version: '1.0.0', + }), + }, + }, + }, + }, + }) + + const link = await tarball({ _resolved: resolve(path, 'node_modules/a/node_modules/b') }, {}) + t.ok(link, 'should retrieve tarball from reading link') + + const target = await tarball({ _resolved: resolve(path, 'packages/a/node_modules/b') }, {}) + t.ok(target, 'should retrieve tarball from reading target') +}) + +t.test('pkg not in a node_modules folder', async t => { + const path = t.testdir({ + packages: { + a: { + 'package.json': json({ + name: 'a', + version: '1.0.0', + }), + }, + }, + }) + + t.throws( + () => tarball({ _resolved: resolve(path, 'packages/a') }, {}), + 'should call regular pacote.tarball method instead' + ) +}) diff --git a/workspaces/libnpmdiff/test/untar.js b/workspaces/libnpmdiff/test/untar.js new file mode 100644 index 000000000..62be1c6ba --- /dev/null +++ b/workspaces/libnpmdiff/test/untar.js @@ -0,0 +1,231 @@ +const { resolve } = require('path') +const t = require('tap') +const pacote = require('pacote') +const untar = require('../lib/untar.js') + +t.test('untar simple package', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/simple-output-2.2.1.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v}`).join('\n'), + 'should return map of filenames to its contents' + ) + t.matchSnapshot(refs.get('a/LICENSE').content, 'should have read contents') +}) + +t.test('untar package with folders', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v}`).join('\n'), + 'should return map of filenames to its contents' + ) + t.matchSnapshot( + refs.get('a/lib/utils/b.js').content, + 'should have read contents' + ) +}) + +t.test('filter files', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/simple-output-2.2.1.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + './LICENSE', + 'missing-file', + 'README.md', + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return map of filenames with valid contents' + ) +}) + +t.test('filter files using glob expressions', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + const cwd = t.testdir({ + lib: { + 'index.js': '', + utils: { + '/b.js': '', + }, + }, + 'package-lock.json': '', + 'package.json': '', + test: { + '/index.js': '', + utils: { + 'b.js': '', + }, + }, + }) + + const _cwd = process.cwd() + process.chdir(cwd) + t.teardown(() => { + process.chdir(_cwd) + }) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + './lib/**', + '*-lock.json', + 'test\\*', // windows-style sep should be normalized + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return map of filenames with valid contents' + ) +}) + +t.test('match files by end of filename', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + '*.js', + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return map of filenames with valid contents' + ) +}) + +t.test('filter files by exact filename', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + 'index.js', + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return no filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return no filenames' + ) +}) + +t.test('match files by simple folder name', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + 'lib', + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return map of filenames with valid contents' + ) +}) + +t.test('match files by simple folder name variation', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/archive.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + './test/', + ], + }) + + t.matchSnapshot([...files].join('\n'), 'should return list of filenames') + t.matchSnapshot( + [...refs.entries()].map(([k, v]) => `${k}: ${!!v.content}`).join('\n'), + 'should return map of filenames with valid contents' + ) +}) + +t.test('filter out all files', async t => { + const item = + await pacote.tarball(resolve('./test/fixtures/simple-output-2.2.1.tgz')) + + const { + files, + refs, + } = await untar({ + item, + prefix: 'a/', + }, { + diffFiles: [ + 'non-matching-pattern', + ], + }) + + t.equal(files.size, 0, 'should have no files') + t.equal(refs.size, 0, 'should have no refs') +}) |