From e5abf2a2171d95bafc0993f337230d2b6633a6ed Mon Sep 17 00:00:00 2001 From: Ruy Adorno Date: Mon, 7 Jun 2021 15:24:33 -0400 Subject: [PATCH] chore(libnpmdiff): added as workspace - Setup ./packages/* as workspaces - Moved source from: https://github.com/npm/libnpmdiff to ./packages/libnpmdiff - Added CI target for workspaces Relates to: https://github.com/npm/statusboard/issues/362 PR-URL: https://github.com/npm/cli/pull/3386 Credit: @ruyadorno Close: #3386 Reviewed-by: @wraithgar --- .github/workflows/ci.yml | 41 ++ node_modules/libnpmdiff | 1 + package-lock.json | 64 ++- package.json | 3 +- packages/libnpmdiff/.eslintrc.json | 207 ++++++++ packages/libnpmdiff/.gitignore | 99 ++++ packages/libnpmdiff/CHANGELOG.md | 30 ++ {node_modules => packages}/libnpmdiff/LICENSE | 0 packages/libnpmdiff/README.md | 98 ++++ .../libnpmdiff/index.js | 0 .../libnpmdiff/lib/format-diff.js | 0 .../libnpmdiff/lib/should-print-patch.js | 0 .../libnpmdiff/lib/tarball.js | 0 .../libnpmdiff/lib/untar.js | 0 .../libnpmdiff/package.json | 12 +- .../test/format-diff.js.test.cjs | 152 ++++++ .../tap-snapshots/test/index.js.test.cjs | 115 +++++ .../tap-snapshots/test/untar.js.test.cjs | 134 +++++ packages/libnpmdiff/test/fixtures/archive.tgz | Bin 0 -> 74564 bytes ...orno-simplistic-pkg-with-folders-1.0.0.tgz | Bin 0 -> 573 bytes .../test/fixtures/simple-output-2.2.1.tgz | Bin 0 -> 2227 bytes packages/libnpmdiff/test/format-diff.js | 483 ++++++++++++++++++ packages/libnpmdiff/test/index.js | 147 ++++++ .../libnpmdiff/test/should-print-patch.js | 28 + packages/libnpmdiff/test/tarball.js | 96 ++++ packages/libnpmdiff/test/untar.js | 231 +++++++++ scripts/bundle-and-gitignore-deps.js | 5 +- 27 files changed, 1912 insertions(+), 34 deletions(-) create mode 120000 node_modules/libnpmdiff create mode 100644 packages/libnpmdiff/.eslintrc.json create mode 100644 packages/libnpmdiff/.gitignore create mode 100644 packages/libnpmdiff/CHANGELOG.md rename {node_modules => packages}/libnpmdiff/LICENSE (100%) create mode 100644 packages/libnpmdiff/README.md rename {node_modules => packages}/libnpmdiff/index.js (100%) rename {node_modules => packages}/libnpmdiff/lib/format-diff.js (100%) rename {node_modules => packages}/libnpmdiff/lib/should-print-patch.js (100%) rename {node_modules => packages}/libnpmdiff/lib/tarball.js (100%) rename {node_modules => packages}/libnpmdiff/lib/untar.js (100%) rename {node_modules => packages}/libnpmdiff/package.json (87%) create mode 100644 packages/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs create mode 100644 packages/libnpmdiff/tap-snapshots/test/index.js.test.cjs create mode 100644 packages/libnpmdiff/tap-snapshots/test/untar.js.test.cjs create mode 100644 packages/libnpmdiff/test/fixtures/archive.tgz create mode 100644 packages/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz create mode 100644 packages/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz create mode 100644 packages/libnpmdiff/test/format-diff.js create mode 100644 packages/libnpmdiff/test/index.js create mode 100644 packages/libnpmdiff/test/should-print-patch.js create mode 100644 packages/libnpmdiff/test/tarball.js create mode 100644 packages/libnpmdiff/test/untar.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78c2926afd6ed..3b622ed82fa78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,47 @@ jobs: env: DEPLOY_VERSION: testing + workspaces-tests: + strategy: + fail-fast: false + matrix: + node-version: [10.x, 12.x, 14.x, 16.x] + platform: + - os: ubuntu-latest + shell: bash + - os: macos-latest + shell: bash + - os: windows-latest + shell: bash + - os: windows-latest + shell: powershell + + runs-on: ${{ matrix.platform.os }} + defaults: + run: + shell: ${{ matrix.platform.shell }} + + steps: + # Checkout the npm/cli repo + - uses: actions/checkout@v2 + + # Installs the specific version of Node.js + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + # Run the installer script + - name: Install dependencies + run: | + node . install --ignore-scripts --no-audit + node . rebuild + + - name: Run workspaces tests + run: node . test -w ./packages -- --no-check-coverage -t600 -Rbase -c + env: + DEPLOY_VERSION: testing + build: strategy: fail-fast: false diff --git a/node_modules/libnpmdiff b/node_modules/libnpmdiff new file mode 120000 index 0000000000000..ae8dd62893029 --- /dev/null +++ b/node_modules/libnpmdiff @@ -0,0 +1 @@ +../packages/libnpmdiff \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b393b7e3f18ef..3f3d1bf0f19ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,7 +78,8 @@ ], "license": "Artistic-2.0", "workspaces": [ - "docs" + "docs", + "packages/*" ], "dependencies": { "@npmcli/arborist": "^2.6.1", @@ -798,7 +799,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@npmcli/disparity-colors/-/disparity-colors-1.0.1.tgz", "integrity": "sha512-kQ1aCTTU45mPXN+pdAaRxlxr3OunkyztjbbxDY/aIcPS5CnCUrx+1+NvA6pTcYR7wmLZe37+Mi5v3nfbwPxq3A==", - "inBundle": true, "dependencies": { "ansi-styles": "^4.3.0" }, @@ -1380,7 +1380,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "inBundle": true, "engines": { "node": ">=8" } @@ -2236,7 +2235,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "inBundle": true, "engines": { "node": ">=0.3.1" } @@ -4632,23 +4630,8 @@ } }, "node_modules/libnpmdiff": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.4.tgz", - "integrity": "sha512-q3zWePOJLHwsLEUjZw3Kyu/MJMYfl4tWCg78Vl6QGSfm4aXBUSVzMzjJ6jGiyarsT4d+1NH4B1gxfs62/+y9iQ==", - "inBundle": true, - "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.1", - "pacote": "^11.3.0", - "tar": "^6.1.0" - }, - "engines": { - "node": ">=10" - } + "resolved": "packages/libnpmdiff", + "link": true }, "node_modules/libnpmexec": { "version": "1.2.0", @@ -10377,6 +10360,31 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "packages/libnpmdiff": { + "version": "2.0.4", + "license": "ISC", + "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": "^11.3.4", + "tar": "^6.1.0" + }, + "devDependencies": { + "eslint": "^7.28.0", + "eslint-plugin-import": "^2.23.4", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-standard": "^5.0.0", + "tap": "^15.0.9" + }, + "engines": { + "node": ">=10" + } } }, "dependencies": { @@ -13706,17 +13714,21 @@ } }, "libnpmdiff": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.4.tgz", - "integrity": "sha512-q3zWePOJLHwsLEUjZw3Kyu/MJMYfl4tWCg78Vl6QGSfm4aXBUSVzMzjJ6jGiyarsT4d+1NH4B1gxfs62/+y9iQ==", + "version": "file:packages/libnpmdiff", "requires": { "@npmcli/disparity-colors": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", "binary-extensions": "^2.2.0", "diff": "^5.0.0", + "eslint": "^7.28.0", + "eslint-plugin-import": "^2.23.4", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-standard": "^5.0.0", "minimatch": "^3.0.4", - "npm-package-arg": "^8.1.1", - "pacote": "^11.3.0", + "npm-package-arg": "^8.1.4", + "pacote": "^11.3.4", + "tap": "^15.0.9", "tar": "^6.1.0" } }, diff --git a/package.json b/package.json index 64568185861bd..a1f5159608cde 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "name": "npm", "description": "a package manager for JavaScript", "workspaces": [ - "docs" + "docs", + "packages/*" ], "files": [ "bin", diff --git a/packages/libnpmdiff/.eslintrc.json b/packages/libnpmdiff/.eslintrc.json new file mode 100644 index 0000000000000..6232a8f82187f --- /dev/null +++ b/packages/libnpmdiff/.eslintrc.json @@ -0,0 +1,207 @@ +{ + "parserOptions": { + "ecmaVersion": 2018, + "ecmaFeatures": {}, + "sourceType": "script" + }, + + "env": { + "es6": true, + "node": true + }, + + "plugins": [ + "import", + "node", + "promise", + "standard" + ], + + "globals": { + "document": "readonly", + "navigator": "readonly", + "window": "readonly" + }, + + "rules": { + "accessor-pairs": "error", + "array-bracket-spacing": ["error", "never"], + "arrow-spacing": ["error", { "before": true, "after": true }], + "block-spacing": ["error", "always"], + "brace-style": ["error", "1tbs", { "allowSingleLine": false }], + "camelcase": ["error", { "properties": "never" }], + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never" + }], + "comma-spacing": ["error", { "before": false, "after": true }], + "comma-style": ["error", "last"], + "computed-property-spacing": ["error", "never"], + "constructor-super": "error", + "curly": ["error", "multi-or-nest"], + "dot-location": ["error", "property"], + "dot-notation": ["error", { "allowKeywords": true }], + "eol-last": "error", + "eqeqeq": ["error", "always", { "null": "ignore" }], + "func-call-spacing": ["error", "never"], + "generator-star-spacing": ["error", { "before": true, "after": true }], + "handle-callback-err": ["error", "^(err|error)$" ], + "indent": ["error", 2, { + "SwitchCase": 1, + "VariableDeclarator": 1, + "outerIIFEBody": 1, + "MemberExpression": 1, + "FunctionDeclaration": { "parameters": 1, "body": 1 }, + "FunctionExpression": { "parameters": 1, "body": 1 }, + "CallExpression": { "arguments": 1 }, + "ArrayExpression": 1, + "ObjectExpression": 1, + "ImportDeclaration": 1, + "flatTernaryExpressions": true, + "ignoreComments": false, + "ignoredNodes": ["TemplateLiteral *"] + }], + "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], + "keyword-spacing": ["error", { "before": true, "after": true }], + "lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], + "new-cap": ["error", { "newIsCap": true, "capIsNew": false, "properties": true }], + "new-parens": "error", + "no-array-constructor": "error", + "no-async-promise-executor": "error", + "no-caller": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "off", + "no-const-assign": "error", + "no-constant-condition": ["error", { "checkLoops": false }], + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-args": "error", + "no-dupe-class-members": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-eval": "error", + "no-ex-assign": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-boolean-cast": "error", + "no-extra-parens": ["error", "functions"], + "no-fallthrough": "error", + "no-floating-decimal": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-implied-eval": "error", + "no-inner-declarations": ["error", "functions"], + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-iterator": "error", + "no-labels": ["error", { "allowLoop": true, "allowSwitch": false }], + "no-lone-blocks": "error", + "no-misleading-character-class": "error", + "no-prototype-builtins": "error", + "no-useless-catch": "error", + "no-mixed-operators": "off", + "no-mixed-spaces-and-tabs": "error", + "no-multi-spaces": "error", + "no-multi-str": "error", + "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], + "no-negated-in-lhs": "error", + "no-new": "off", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-symbol": "error", + "no-new-wrappers": "error", + "no-obj-calls": "error", + "no-octal": "error", + "no-octal-escape": "error", + "no-path-concat": "error", + "no-proto": "error", + "no-redeclare": ["error", { "builtinGlobals": false }], + "no-regex-spaces": "error", + "no-return-assign": "off", + "no-self-assign": "off", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-tabs": "error", + "no-template-curly-in-string": "error", + "no-this-before-super": "error", + "no-throw-literal": "off", + "no-trailing-spaces": "error", + "no-undef": "error", + "no-undef-init": "error", + "no-unexpected-multiline": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": ["error", { "defaultAssignment": false }], + "no-unreachable": "error", + "no-unsafe-finally": 0, + "no-unsafe-negation": "error", + "no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true }], + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }], + "no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }], + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-escape": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-void": "error", + "no-whitespace-before-property": "error", + "no-with": "error", + "nonblock-statement-body-position": [2, "below"], + "object-curly-newline": "off", + "object-curly-spacing": "off", + "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], + "one-var": ["error", { "initialized": "never" }], + "operator-linebreak": "off", + "padded-blocks": ["error", { "blocks": "never", "switches": "never", "classes": "never" }], + "prefer-const": ["error", {"destructuring": "all"}], + "prefer-promise-reject-errors": "error", + "quote-props": ["error", "as-needed"], + "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], + "rest-spread-spacing": ["error", "never"], + "semi": ["error", "never"], + "semi-spacing": ["error", { "before": false, "after": true }], + "space-before-blocks": ["error", "always"], + "space-before-function-paren": ["error", "always"], + "space-in-parens": ["error", "never"], + "space-infix-ops": "error", + "space-unary-ops": ["error", { "words": true, "nonwords": false }], + "spaced-comment": ["error", "always", { + "line": { "markers": ["*package", "!", "/", ",", "="] }, + "block": { "balanced": true, "markers": ["*package", "!", ",", ":", "::", "flow-include"], "exceptions": ["*"] } + }], + "symbol-description": "error", + "template-curly-spacing": ["error", "never"], + "template-tag-spacing": ["error", "never"], + "unicode-bom": ["error", "never"], + "use-isnan": "error", + "valid-typeof": ["error", { "requireStringLiterals": true }], + "wrap-iife": ["error", "any", { "functionPrototypeMethods": true }], + "yield-star-spacing": ["error", "both"], + "yoda": ["error", "never"], + + "import/export": "error", + "import/first": "error", + "import/no-absolute-path": ["error", { "esmodule": true, "commonjs": true, "amd": false }], + "import/no-duplicates": "error", + "import/no-named-default": "error", + "import/no-webpack-loader-syntax": "error", + + "node/no-deprecated-api": "error", + "node/process-exit-as-throw": "error", + + "promise/param-names": "off", + + "standard/no-callback-literal": "error" + } +} diff --git a/packages/libnpmdiff/.gitignore b/packages/libnpmdiff/.gitignore new file mode 100644 index 0000000000000..0aba557bf2857 --- /dev/null +++ b/packages/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/packages/libnpmdiff/CHANGELOG.md b/packages/libnpmdiff/CHANGELOG.md new file mode 100644 index 0000000000000..b93b15b7b1113 --- /dev/null +++ b/packages/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/node_modules/libnpmdiff/LICENSE b/packages/libnpmdiff/LICENSE similarity index 100% rename from node_modules/libnpmdiff/LICENSE rename to packages/libnpmdiff/LICENSE diff --git a/packages/libnpmdiff/README.md b/packages/libnpmdiff/README.md new file mode 100644 index 0000000000000..bc260ad15ce12 --- /dev/null +++ b/packages/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 ", + "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://www.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` + +Fetches the registry tarballs and compare files between a spec `a` and spec `b`. **npm** spec types are usually described in `@` 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 `: Should add ANSI colors to string output? Defaults to `false`. +- `tagVersionPrefix `: What prefix should be used to define version numbers. Defaults to `v` +- `diffUnified `: How many lines of code to print before/after each diff. Defaults to `3`. +- `diffFiles >`: If set only prints patches for the files listed in this array (also accepts globs). Defaults to `undefined`. +- `diffIgnoreAllSpace `: Whether or not should ignore changes in whitespace (very useful to avoid indentation changes extra diff lines). Defaults to `false`. +- `diffNameOnly `: Prints only file names and no patch diffs. Defaults to `false`. +- `diffNoPrefix `: If true then skips printing any prefixes in filenames. Defaults to `false`. +- `diffSrcPrefix `: Prefix to be used in the filenames from `a`. Defaults to `a/`. +- `diffDstPrefix `: Prefix to be used in the filenames from `b`. Defaults to `b/`. +- `diffText `: 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/node_modules/libnpmdiff/index.js b/packages/libnpmdiff/index.js similarity index 100% rename from node_modules/libnpmdiff/index.js rename to packages/libnpmdiff/index.js diff --git a/node_modules/libnpmdiff/lib/format-diff.js b/packages/libnpmdiff/lib/format-diff.js similarity index 100% rename from node_modules/libnpmdiff/lib/format-diff.js rename to packages/libnpmdiff/lib/format-diff.js diff --git a/node_modules/libnpmdiff/lib/should-print-patch.js b/packages/libnpmdiff/lib/should-print-patch.js similarity index 100% rename from node_modules/libnpmdiff/lib/should-print-patch.js rename to packages/libnpmdiff/lib/should-print-patch.js diff --git a/node_modules/libnpmdiff/lib/tarball.js b/packages/libnpmdiff/lib/tarball.js similarity index 100% rename from node_modules/libnpmdiff/lib/tarball.js rename to packages/libnpmdiff/lib/tarball.js diff --git a/node_modules/libnpmdiff/lib/untar.js b/packages/libnpmdiff/lib/untar.js similarity index 100% rename from node_modules/libnpmdiff/lib/untar.js rename to packages/libnpmdiff/lib/untar.js diff --git a/node_modules/libnpmdiff/package.json b/packages/libnpmdiff/package.json similarity index 87% rename from node_modules/libnpmdiff/package.json rename to packages/libnpmdiff/package.json index aa13954c63010..53fd5d4befd5e 100644 --- a/node_modules/libnpmdiff/package.json +++ b/packages/libnpmdiff/package.json @@ -46,12 +46,12 @@ ] }, "devDependencies": { - "eslint": "^7.18.0", - "eslint-plugin-import": "^2.22.1", + "eslint": "^7.28.0", + "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", - "tap": "^14.11.0" + "tap": "^15.0.9" }, "dependencies": { "@npmcli/disparity-colors": "^1.0.1", @@ -59,8 +59,8 @@ "binary-extensions": "^2.2.0", "diff": "^5.0.0", "minimatch": "^3.0.4", - "npm-package-arg": "^8.1.1", - "pacote": "^11.3.0", + "npm-package-arg": "^8.1.4", + "pacote": "^11.3.4", "tar": "^6.1.0" } } diff --git a/packages/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs b/packages/libnpmdiff/tap-snapshots/test/format-diff.js.test.cjs new file mode 100644 index 0000000000000..f735d8925820a --- /dev/null +++ b/packages/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`] = ` +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 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/packages/libnpmdiff/tap-snapshots/test/index.js.test.cjs b/packages/libnpmdiff/tap-snapshots/test/index.js.test.cjs new file mode 100644 index 0000000000000..21db3deac4d70 --- /dev/null +++ b/packages/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/packages/libnpmdiff/tap-snapshots/test/untar.js.test.cjs b/packages/libnpmdiff/tap-snapshots/test/untar.js.test.cjs new file mode 100644 index 0000000000000..b1092feb6ee8c --- /dev/null +++ b/packages/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/packages/libnpmdiff/test/fixtures/archive.tgz b/packages/libnpmdiff/test/fixtures/archive.tgz new file mode 100644 index 0000000000000000000000000000000000000000..843a611239bbb9e8f3edc2477b7e49347297d850 GIT binary patch literal 74564 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%0D<47F@{kZsJ?ru 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 T+E@E(f06b-;D4KR0Qv*~$S*PX literal 0 HcmV?d00001 diff --git a/packages/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz b/packages/libnpmdiff/test/fixtures/ruyadorno-simplistic-pkg-with-folders-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..11bbb44c4e9a42c832fee5d973207d50116aa826 GIT binary patch literal 573 zcmV-D0>b?tiwFP!00000|Ls=6j+-zL-7{Zdae-9CKo(eO)vmTuFFo{9^;Wf#0XMh- z+r=ikQI&tM4I~?yR2-VHisYv#CgT|xCwcD;N7)K3v0swZZzL&&pTeKw=Bx;UAc-Py zD&ArdgPYqp3BoW*0+pvO?xKZS&)ddnG(w_Uwer9B>5c;S>y@Blc~fGDpE;|g0Pld4 zI@W79WiT0d&wq)9yp#Zp@PDB>Kji-+j1B)MLB^<%Kq}(b_*X+}>^c&0XJ%Ov>i>l6 zh=Mg`4hpo!Aay*Q`v87h0Cc74VJU>ZdJVlq-3tH-RSGKI)YAgfM`-TOtnJLm%`1W* z_&=dJem=YVkMciU1V{cKC6VF(6zE8dwDBXyIz{Orm93+JK}!Mh)LZIVTf^lm!@&P9 zBmiUnpPZQggFD0jDKPH;vLJLRKtBdNv3hkp1PD^`k_bt%zovmhTNw-jFBK4ZT6_Mn zX$FSNV#oh}RX-D>=YGZr|KsrXi2rdE8~#rL&0(9O3fuRBWOtjuvOY;E@TT3r!Fby)}l7xMG_B&m$LZtn)6M`<p zz3=b$b=;qI&6Fg=RJ32hr;lHDXIX#1T!{{*8Nr8*BCZp^hY<|b+1nP)=wo1DaBcVl L*iDpP02lxO>6#Hd literal 0 HcmV?d00001 diff --git a/packages/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz b/packages/libnpmdiff/test/fixtures/simple-output-2.2.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8d442f4c1c0780f8501206444db6eb7d855357be GIT binary patch literal 2227 zcmV;k2u$}MiwFP!00000|Lqy=ZrjN5e)B1&_Telw5+y6K4{+c+$Tl4l)rUdJmj-qY zrpT4F4#`z_mv)4!U#9;G^eOr%eUi@XlKOC*#A%uY0lct5?(TfgaAs#U3!^vTgf%+; zq1PLDKYWSNY&LhFJ%jt!akRFZP59yI&Teyidv~{qaLw&zYiAdqUqqTaBUd_W`JIg6ltI@YLWf5zQw%CNsXES~n9U`hOf^)ZDg^l)+Od$CfQroLp?P&9ks{8M0&s zCl+Wj!|{qmnpOyWERsarkZO_0Von;VJ;x!Jha+*#j4sy8nb0Vz6@eTuTY9zFQqwR= zV8jZ8Azz+3v{b4ii9LQyndV^vGa(JXyK1_~|HOkq|9JSa9eCgm;56v}<{x=Su+bji ze4_?0{ozUfYzPPuw0px>&_9ND?-l&T?;X{^`|D}o4F=E;9KU|# zob)?K9xfhuC|UcU<5^ay)M2OXcWZFe?zUfe2CI)$0*An?bnx=Tqb1_k#=pa%-|vwc zhyC6#z-bM&42BETm;S)3K|AmVWRT;a->o@hPQ>UNB8b=XEEO^}ta$`MXnr>E76jqQ zYj=t3lZ!Q&(KzuEfvo%;WC>#_cSm4TH(X;AZY zBoYPtSR4PAbIB@OiI~)j<*n*-$HHM5Pu{FTnFZQ+oJIrcb$pKV%#>Cv2T{b7s-$XC zg+CmiqM0YmWpAlCC|Bti%dLAOxn`Aum0KXe6W9X$djg0}lZ%YF+^&#kQVRJY;s|hu zxIuXUX`YS6r-@A%4!ju1sGi#4>xP^^BgyB@!y6a#|K8?)_?x> zkFDp6)il)6w2vp3Pz$L7HeqiOsB!-Hi;F*QE7kLh*T4Vxvgf`-R$a03sd(CA(9r||GX`onS5KCzfO4HeS z&F)}GukGO6>{#2;s|k8~p%|rS5rp(gkwj0DkDlaN=N7-_Z=IGN$5$F<{V$$)0ub5% zo5zRi|Mm{m`t|z1y|dGLtp8tQP=eXW!jx_7!G_}LEMaxbF0)*3P(s+a#x@i)IqkOG zmb-0NQ*7I5sXR5>Rnw2-^0`T%%T1iVHUKP}@XUbNM883Khv@vWzuhFTTLTs|6-hqR z1>e3Ar%YR>Bep59Ve_43jy*D);XYwZwVtWHMq`2UwS(e*qpJ&i_wayYJ-x=FYRn{_m@d zO<2njj&uH}^WJF}ioE~2Qs$RY7~}C^oXfe(MdM1{$t~)7Qe6UPv-C>2LQcwa#X5AA zxE$m>iD95aovS6Lmfx*Yd8)uGozX4+MBD=Feu$j z0bTIhU6_kpmS&Gb9l~gi{p1_k;<_-JBtN7gdIOj1Ry5b<9Q(CGC%OyA5HOygR!*@P z{U=$ND%1x(KE|HXUhMN|YQPN72}2sxFk;}~w(FozkydOulc;NQl4)cb+Mh9sPSp@w zixp}Pv2oROM^Ev9!dfMS{jGrFbnzpM=~iLUzE@Ds9D|%TEn%xfIOA0p2CP)k#1D*E zB5wA`=$p_b4%SNj;*&K({Vy>_FzLK&J+}hrx{Y*Rw;o-_qG3~X&R`YVTWGYri?Ab9 z_@$a4!gl^n$kP6;X@*PYuJ!M58tOB})W8VZ5fdLB;getSpp?uj%W&U>Gi41+7N@s_ zW7?AQ`^-@2pJS8q zmqx5Km{MO?%H#+C{wK7Wt!+489-9}L`Rqvh9>@1T{tK9ZW4Qn%007R& BVv_&> literal 0 HcmV?d00001 diff --git a/packages/libnpmdiff/test/format-diff.js b/packages/libnpmdiff/test/format-diff.js new file mode 100644 index 0000000000000..f2fc7c77d7f11 --- /dev/null +++ b/packages/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/packages/libnpmdiff/test/index.js b/packages/libnpmdiff/test/index.js new file mode 100644 index 0000000000000..88b474c111f15 --- /dev/null +++ b/packages/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/packages/libnpmdiff/test/should-print-patch.js b/packages/libnpmdiff/test/should-print-patch.js new file mode 100644 index 0000000000000..97b15787d3933 --- /dev/null +++ b/packages/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/packages/libnpmdiff/test/tarball.js b/packages/libnpmdiff/test/tarball.js new file mode 100644 index 0000000000000..3a959be6e53bc --- /dev/null +++ b/packages/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/packages/libnpmdiff/test/untar.js b/packages/libnpmdiff/test/untar.js new file mode 100644 index 0000000000000..62be1c6ba9003 --- /dev/null +++ b/packages/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') +}) diff --git a/scripts/bundle-and-gitignore-deps.js b/scripts/bundle-and-gitignore-deps.js index 84a3ab3ad9ef0..407b9e5982514 100644 --- a/scripts/bundle-and-gitignore-deps.js +++ b/scripts/bundle-and-gitignore-deps.js @@ -10,7 +10,10 @@ const shouldIgnore = [] arb.loadVirtual().then(tree => { for (const node of tree.children.values()) { - if (node.dev || node.isLink) { + const has = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) + const nonProdWorkspace = + node.isWorkspace && !(has(tree.package.dependencies, node.name)) + if (node.dev || nonProdWorkspace) { console.error('ignore', node.name) shouldIgnore.push(node.name) } else if (tree.edgesOut.has(node.name)) {