diff --git a/README.md b/README.md index 8f992b0..012546d 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,8 @@ 888 888 888 Y88b. Y88b d88P Y88b. 888 888 888 888 888 "Y888888 "Y8888P "Y8888P" "Y8888P "Y888888 888 888 -[PacScan](https://github.com/Skelp/node-pacscan) provide information about all available packages for your module at -runtime. - -> This library is very much still in early development! Watch this space. +[PacScan](https://github.com/Skelp/node-pacscan) provides information about all available packages for your module at +runtime by scanning `node_modules` as opposed to digging into dependency trees. [![Build](https://img.shields.io/travis/Skelp/node-pacscan/develop.svg?style=flat-square)](https://travis-ci.org/Skelp/node-pacscan) [![Coverage](https://img.shields.io/coveralls/Skelp/node-pacscan/develop.svg?style=flat-square)](https://coveralls.io/github/Skelp/node-pacscan) @@ -37,11 +35,65 @@ You'll need to have at least [Node.js](https://nodejs.org) 4 or newer. ### `pacscan([options])` -TODO: Document +Scans for all packages available to your module asynchronously, returning a `Promise` to retrieve all of the package +information, each of which will be in a format similar to the following: + +``` javascript +{ + // The directory of the package + directory: '/path/to/my-example-package/node_modules/example-server', + // The file path of the "main" file for the package or null if it has none + main: '/path/to/my-example-package/node_modules/example-server/server.js', + // The name of the package + name: 'example-server', + // The version of the package + version: '3.2.1' +} +``` + +The `options` parameter is entirely optional and supports the following: + +| Option | Description | Default Value | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `includeParents` | Whether the highest level package directory should be scanned or only the lowest level base directory. | `false` | +| `knockknock` | Any options to be passed to [KnockKnock](https://github.com/Skelp/node-knockknock). `limit` will always be overridden to `1`. | `null` | +| `path` | The file/directory path from where to derive the base directory to be scanned. Path to module that called PacScan will be used when `null`. | `null` | + +If you only want to list the packages available to your module/package: + +``` javascript +const pacscan = require('pacscan') + +module.exports = function() { + pacscan() + .then((packages) => { + console.log(`${packages.length} packages found`) + + // ... + }) +} +``` + +However, if you're calling PacScan from within a library that is most likely being included in another package as a +dependency. In these cases, you might want to know all of the packages available in the base package (i.e. the highest +level package that is not a dependency itself). All that you need to do for this is to enable the `includeParents` +option. ### `pacscan.sync([options])` -TODO: Document +A synchronous alternative to `pacscan([options])`. + +``` javascript +const pacscan = require('pacscan') + +module.exports = function() { + const packages = pacscan.sync() + + console.log(`${packages.length} packages found`) + + // ... +} +``` ### `pacscan.version` @@ -51,7 +103,7 @@ The current version of PacScan. const pacscan = require('pacscan') pacscan.version -=> "0.1.0alpha" +=> "0.1.0" ``` ## Bugs diff --git a/package.json b/package.json index 6f4493d..f643960 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pacscan", - "version": "0.1.0alpha", + "version": "0.1.0", "description": "Scans for available packages", "homepage": "https://github.com/Skelp/node-pacscan", "bugs": { @@ -23,6 +23,8 @@ }, "dependencies": { "debug": "*", + "glob": "^7.1.1", + "knockknock": "^0.2.0", "pkg-dir": "^1.0.0" }, "devDependencies": { @@ -31,15 +33,17 @@ "eslint": "^3.16.0", "eslint-config-skelp": "^0.1.5", "istanbul": "^0.4.5", - "mocha": "^3.2.0" + "mkdirp": "^0.5.1", + "mocha": "^3.2.0", + "ncp": "^2.0.0", + "tmp": "^0.0.31" }, "main": "src/pacscan.js", "scripts": { "report-coverage": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", - "test": "npm run test-lint && npm run test-suite && npm run test-coverage", - "test-coverage": "istanbul check-coverage", - "test-lint": "eslint \"src/**/*.js\" \"test/**/*.js\"", - "test-suite": "istanbul cover _mocha -- -R spec \"test/**/*.spec.js\"" + "pretest": "eslint \"src/**/*.js\" \"test/**/*.js\"", + "test": "istanbul cover _mocha -- -R spec \"test/**/*.spec.js\"", + "posttest": "istanbul check-coverage" }, "engines": { "node": ">=4" diff --git a/src/pacscan.js b/src/pacscan.js index f72aba5..e7793ba 100644 --- a/src/pacscan.js +++ b/src/pacscan.js @@ -22,8 +22,536 @@ 'use strict' +const debug = require('debug')('pacscan') +const fs = require('fs') +const glob = require('glob') +const path = require('path') +const pkgDir = require('pkg-dir') +const whoIsThere = require('knockknock') + const version = require('../package.json').version +/** + * A cache containing the available package.json file paths mapped to directory paths. + * + * The intention of this cache is to speed up available package lookups for repeat callers by avoiding file system + * searches. + * + * @private + * @type {Map.} + */ +const availablePackagesCache = new Map() + +/** + * Scans for all available packages at either a given file/directory or at the module that called PacScan. It will find + * the base package for that file path and then find and extract simple information from all accessible + * package.json files, with the option to even find packages belonging to parent packages, where + * applicable. + * + * @private + */ +class PacScan { + + /** + * Asynchronously finds all files that match the specified patterns using the glob options + * provided and passes the paths of these files to the callback function. + * + * This method returns a Promise chained using the callback function so that it can be + * returned by {@link PacScan#scan}. + * + * @param {string[]} patterns - the glob patterns to be used to target package.json files + * @param {Object} options - the glob options to be used + * @param {pacscan~PackagePathsCallback} callback - the function to be called with the package.json file + * paths + * @return {Promise.} The result of calling callback. + * @private + * @static + */ + static _findPackagePaths(patterns, options, callback) { + const searches = patterns.map((pattern) => new Promise((resolve, reject) => { + glob(pattern, options, (error, filePaths) => { + /* istanbul ignore if */ + if (error) { + reject(error) + } else { + resolve(filePaths) + } + }) + })) + + return Promise.all(searches) + .then((values) => { + let filePaths = [] + values.forEach((value) => { + filePaths = filePaths.concat(value) + }) + + return filePaths + }) + .then(callback) + } + + /** + * Synchronously finds all files that match the specified patterns using the glob options + * provided and passes the paths of these files to the callback function. + * + * This method returns the return value of the callback function so that it can be returned by + * {@link PacScan#scan}. + * + * @param {string[]} patterns - the glob patterns to be used to target package.json files + * @param {Object} options - the glob options to be used + * @param {pacscan~PackagePathsCallback} callback - the function to be called with the package.json file + * paths + * @return {pacscan~Package[]} The result of calling callback. + * @private + * @static + */ + static _findPackagePathsSync(patterns, options, callback) { + let filePaths = [] + patterns.forEach((pattern) => { + filePaths = filePaths.concat(glob.sync(pattern, options)) + }) + + return callback(filePaths) + } + + /** + * Returns the information for the package installed in the directory at the path provided. + * + * This information contains dirPath as well as the name, version, and + * (absolute) path of the main file (if any) read from the package.json file. + * + * This method should only be called when it is known that dirPath contains a package.json + * file. + * + * @param {string} dirPath - the path of the installation directory for the package whose information is to be + * returned + * @return {pacscan~Package} The information for the package installed within dirPath. + * @private + * @static + */ + static _getPackage(dirPath) { + debug('Attempting to retrieve information for package installed in directory: %s', dirPath) + + const pkg = require(path.join(dirPath, 'package.json')) + + return { + directory: dirPath, + main: pkg.main ? path.join(dirPath, pkg.main) : null, + name: pkg.name, + version: pkg.version + } + } + + /** + * Parses the optional input options provided, normalizing options and applying default values, where + * needed. + * + * @param {?pacscan~Options} options - the input options to be parsed (may be null if none were provided) + * @return {pacscan~Options} A new options object parsed from options. + * @private + * @static + */ + static _parseOptions(options) { + if (!options) { + options = {} + } + + return { + includeParents: options.includeParents, + knockknock: options.knockknock, + path: options.path + } + } + + /** + * Creates an instance of {@link PacScan} using the optional options provided. + * + * sync can be used to control whether package searches are performed synchronously or asynchronously. + * + * @param {boolean} sync - true if package searches should be synchronous or false if they + * should be asynchronous + * @param {pacscan~Options} [options] - the options to be used (may be null) + * @public + */ + constructor(sync, options) { + /** + * Whether package searches initiated by this {@link PacScan} should be made synchronously. + * + * @private + * @type {boolean} + */ + this._sync = sync + + /** + * The parsed options for this {@link PacScan}. + * + * @private + * @type {pacscan~Options} + */ + this._options = PacScan._parseOptions(options) + } + + /** + * Searches for all available packages within the base directory and returns a summary of the information for these + * packages. + * + * This method will directly return the information for all available packages if this {@link PacScan} is synchronous. + * Otherwise, this method will return a Promise which will be resolved with the information for all + * available packages once they have been found. + * + * @return {pacscan~Package[]|Promise.} The information for all available packages (or a + * Promise resolved with them when asynchronous). + * @public + */ + scan() { + return this._resolveBaseDirectory((dirPath) => { + debug('Scanning for available packages within directory: %s', dirPath) + + return this._findAvailablePackagePaths(dirPath, (filePaths) => { + debug('Found %d available packages within directory: %s', filePaths.length, dirPath) + + return filePaths.map((filePath) => { + const pkg = PacScan._getPackage(path.dirname(filePath)) + + return Object.assign({}, pkg) + }) + }) + }) + } + + /** + * Finds all package.json files that are available within the directory provided and passes the paths of + * these files to the callback function. + * + * The flow for this method differs based on whether this {@link PacScan} is synchronous. + * + * @param {string} dirPath - the path to the directory to be searched + * @param {pacscan~PackagePathsCallback} callback - the function to be called with the package.json file + * paths + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _findAvailablePackagePaths(dirPath, callback) { + if (availablePackagesCache.has(dirPath)) { + return callback(availablePackagesCache.get(dirPath)) + } + + debug('Attempting to find all packages files within directory: %s', dirPath) + + const options = { cwd: dirPath, nodir: true, nosort: true } + const patterns = [ + '**/node_modules/*/package.json', + '**/node_modules/@*/*/package.json' + ] + const packagePathFinder = PacScan[this._sync ? '_findPackagePathsSync' : '_findPackagePaths'] + + return packagePathFinder(patterns, options, (filePaths) => this._isPackageDirectory(dirPath, (isPackage) => { + if (isPackage) { + filePaths.unshift('package.json') + } + + filePaths = filePaths + .sort() + .map((filePath) => path.join(dirPath, filePath)) + + availablePackagesCache.set(dirPath, filePaths) + + debug('Found %d packages files within directory: %s', filePaths.length, dirPath) + + return callback(filePaths) + })) + } + + /** + * Finds the base directory from the specified filePath and passes the path to the base directory to the + * callback function. + * + * The base directory is basically the highest level package directory. This is determined by finding package + * directories and climbing the node_modules directories until it can't anymore. At that point, the base + * directory is found. + * + * This method should only be called when the includeParents option is enabled. + * + * @param {string} filePath - the file path from where the base directory should be found + * @param {pacscan~BaseDirectoryCallback} callback - the function to be called with the path to the base directory + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _findBaseDirectory(filePath, callback) { + return this._findPackageDirectory(filePath, (dirPath) => { + if (dirPath == null) { + return callback(null) + } + + let parentDirPath = path.dirname(dirPath) + let parentDirName = path.basename(parentDirPath) + + if (parentDirName.charAt(0) === '@') { + parentDirPath = path.dirname(parentDirPath) + parentDirName = path.basename(parentDirPath) + } + + if (parentDirName === 'node_modules') { + return this._findBaseDirectory(parentDirPath, (parentPkgDirPath) => { + if (parentPkgDirPath != null) { + dirPath = parentPkgDirPath + } + + return callback(dirPath) + }) + } + + return callback(dirPath) + }) + } + + /** + * Finds the information for module that was responsible for calling PacScan and passes it to the + * callback function. + * + * Consumers can control what modules/packages are considering during this search via the knockknock + * option, however, the limit will always be overridden to 1. + * + * The flow for this method differs based on whether this {@link PacScan} is synchronous. + * + * @param {pacscan~FindCallerCallback} callback - the function to be called with the caller information + * @return {pacscan~Package[]|Promise.} The result of calling callback + * @private + */ + _findCaller(callback) { + const excludes = [ 'pacscan' ] + const options = Object.assign({}, this._options.knockknock, { limit: 1 }) + + options.excludes = options.excludes ? excludes.concat(options.excludes) : excludes + + if (this._sync) { + return callback(whoIsThere.sync(options)[0]) + } + + return whoIsThere(options) + .then((callers) => callers[0]) + .then(callback) + } + + /** + * Finds the installation directory for the package containing the specified filePath and passes the + * directory path to the callback function. + * + * If filePath does not exist within a package, the directory path will be null. + * + * The flow for this method differs based on whether this {@link PacScan} is synchronous. + * + * @param {string} filePath - the path of the file whose package directory is to be found + * @param {pacscan~FindPackageDirectoryCallback} callback - the function to be called with the package directory path + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _findPackageDirectory(filePath, callback) { + if (this._sync) { + return callback(pkgDir.sync(filePath)) + } + + return pkgDir(filePath).then(callback) + } + + /** + * Determines whether the specified filePath is a directory and passes the result to the + * callback function. + * + * @param {string} filePath - the path of the file to be checked + * @param {pacscan~IsDirectoryCallback} callback - the function to be called with the result + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _isDirectory(filePath, callback) { + if (this._sync) { + return callback(fs.statSync(filePath).isDirectory()) + } + + return new Promise((resolve, reject) => { + fs.stat(filePath, (error, stats) => { + /* istanbul ignore if */ + if (error) { + reject(error) + } else { + resolve(stats.isDirectory()) + } + }) + }) + .then(callback) + } + + /** + * Determines whether the specified filePath is a package installation directory and passes the result + * to the callback function. + * + * @param {string} filePath - the path of the fiile to be checked + * @param {pacscan~IsPackageDirectoryCallback} callback - the function to be called with the result + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _isPackageDirectory(filePath, callback) { + return this._findPackageDirectory(filePath, (dirPath) => callback(filePath === dirPath)) + } + + /** + * Resolves the base directory from where the package scan should originate and passes the directory path to the + * callback function. + * + * If the path option is specified, it is treated as the base file (even if it's not a file). Otherwise, + * the module that was responsible for calling PacScan is found and will be treated as the base file instead. + * + * If the base file belongs to a package, its directory will be used in the next step. Otherwise, the directory of the + * base file is used instead. + * + * At this stage, we have a base directory, however, if the includeParents option is enabled, the + * dependency tree (based on directory structure, not package.json) is climbed to find the highest level + * base directory, where possible. + * + * @param {pacscan~BaseDirectoryCallback} callback - the function to be called with the base directory path + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _resolveBaseDirectory(callback) { + let packageResolver + if (this._options.path != null) { + packageResolver = this._resolvePackageFromPath.bind(this) + } else { + packageResolver = this._resolvePackageFromCaller.bind(this) + } + + return packageResolver((filePath, pkg) => { + if (filePath == null) { + throw new Error('Could not resolve base directory as file was missing') + } + + if (pkg == null) { + return this._isDirectory(filePath, (isDirectory) => { + const dirPath = isDirectory ? filePath : path.dirname(filePath) + + debug('Unable to find package containing file "%s" so using directory as base: %s', filePath, dirPath) + + return callback(dirPath) + }) + } + + const dirPath = pkg.directory + + debug('Found package "%s" containing file: %s', pkg.name, filePath) + + if (!this._options.includeParents) { + debug('Using installation directory for package containing file as base: %s', dirPath) + + return callback(dirPath) + } + + debug('Attempting to find base parent package installation directory from package "%s" to use as base', pkg.name) + + return this._findBaseDirectory(dirPath, callback) + }) + } + + /** + * Resolves the package from the module that was responsible for calling PacScan and passes the module file path and + * package information to the callback function. + * + * If no caller information could be found, both the file path and package information will be null or, + * if the calling module does not belong to a package, only the package information will be null. + * + * @param {pacscan~ResolvePackageCallback} callback - the function to be called with the file path and package + * information derived from the calling module + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _resolvePackageFromCaller(callback) { + return this._findCaller((caller) => { + if (caller == null) { + return callback(null, null) + } + + return callback(caller.file, caller.package) + }) + } + + /** + * Resolves the package from the path option and passes the path option and package + * information to the callback function. + * + * If the path option does not belong to a package, the package information will be null. + * + * @param {pacscan~ResolvePackageCallback} callback - the function to be called with the path option and + * package information + * @return {pacscan~Package[]|Promise.} The result of calling callback. + * @private + */ + _resolvePackageFromPath(callback) { + const filePath = this._options.path + + return this._findPackageDirectory(filePath, (dirPath) => { + const pkg = dirPath != null ? PacScan._getPackage(dirPath) : null + + return callback(filePath, pkg) + }) + } + +} + +/** + * Asynchronously resolves the base directory from either the path option or the module that was + * responsible for calling PacScan and then scans this directory for all packages available within it. + * + * The scan can also include packages from parent packages (for cases where the target module exists within a depedent + * package - e.g. exists within another package's node_modules directory) by enabling the + * includeParents option. + * + * The resolution of the calling module can be controlled at a more granular level by specifying knockknock + * options. + * + * @param {pacscan~Options} [options] - the options to be used (may be null) + * @return {Promise.} A Promise for retrieving the information for all available + * packages. + * @public + * @static + */ +module.exports = function scan(options) { + return Promise.resolve(new PacScan(false, options).scan()) +} + +/** + * Clears the cache containing available package.json file paths mapped to directory paths which is used to + * speed up package lookups for repeat callers by avoiding file system searches. + * + * This is primarily intended for testing purposes. + * + * @return {void} + * @protected + * @static + */ +module.exports.clearCache = function clearCache() { + availablePackagesCache.clear() +} + +/** + * Synchronously resolves the base directory from either the path option or the module that was responsible + * for calling PacScan and then scans this directory for all packages available within it. + * + * The scan can also include packages from parent packages (for cases where the target module exists within a depedent + * package - e.g. exists within another package's node_modules directory) by enabling the + * includeParents option. + * + * The resolution of the calling module can be controlled at a more granular level by specifying knockknock + * options. + * + * @param {pacscan~Options} [options] - the options to be used (may be null) + * @return {pacscan~Package[]} The information for all available packages. + * @public + * @static + */ +module.exports.sync = function scanSync(options) { + return new PacScan(true, options).scan() +} + /** * The current version of PacScan. * @@ -32,3 +560,87 @@ const version = require('../package.json').version * @type {string} */ module.exports.version = version + +/** + * Called with the path of a base directory. + * + * @callback pacscan~BaseDirectoryCallback + * @param {?string} dirPath - the path to the base directory (may be null if none could be found) + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with the information for the caller that was responsible for calling PacScan. + * + * @callback pacscan~FindCallerCallback + * @param {?knockknock~Caller} caller - the caller information (may be null if no caller could be found) + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with the path to the package installation directory containing a file. + * + * @callback pacscan~FindPackageDirectoryCallback + * @param {?string} dirPath - the path to the package installation directory (may be null if the file does + * not belong to a package) + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with whether a file path points to an existing directory. + * + * @callback pacscan~IsDirectoryCallback + * @param {boolean} isDirectory - true if the file path points to a directory; otherwise false + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with whether a file path points to a package installation directory. + * + * @callback pacscan~IsPackageDirectoryCallback + * @param {boolean} isPackage - true if the file path points to a pacakge installation directory; otherwise + * false + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with the absolute paths for all package.json files found within a directory. + * + * @callback pacscan~PackagePathsCallback + * @param {string[]} filePaths - the paths to all available package.json files + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Called with the file path and package information resolved from a file path. + * + * @callback pacscan~ResolvePackageCallback + * @param {?string} filePath - the path to the target file (may be null if it could not be resolved) + * @param {?pacscan~Package} pkg - the package information (may be null if it could not be resolved or the + * file does not belong to a package) + * @return {pacscan~Package[]|Promise.} The scan result. + */ + +/** + * Contains some basic information for an individual package. + * + * @typedef {Object} pacscan~Package + * @property {string} directory - The path to the installation directory of the package. + * @property {?string} main - The path to the main file for the package (may be null if it has no + * main entry). + * @property {string} name - The name of the package. + * @property {string} version - The version of the package. + */ + +/** + * The options to be used to scan for packages. + * + * @typedef {Object} pacscan~Options + * @property {boolean} [includeParents] - true if the highest level package directory should be scanned or + * false to scan only the initial base directory. + * @property {knockknock~Options} [knockknock] - The options to be passed to knockknock when attempting to + * determine the calling module (limit will always be overridden to 1). + * @property {string} [path] - The path of the file/directory from which the base directory to be scanned is derived. + * The base directory should be derived from the module that was responsible for calling PacScan if this is + * null. + */ diff --git a/test/fixtures/.gitignore b/test/fixtures/.gitignore new file mode 100644 index 0000000..ddf3424 --- /dev/null +++ b/test/fixtures/.gitignore @@ -0,0 +1 @@ +!node_modules/ diff --git a/test/fixtures/flat/index.js b/test/fixtures/flat/index.js new file mode 100644 index 0000000..6edce89 --- /dev/null +++ b/test/fixtures/flat/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function flatFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function flatSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/@baz/buzz/package.json b/test/fixtures/flat/node_modules/@baz/buzz/package.json new file mode 100644 index 0000000..4ad0088 --- /dev/null +++ b/test/fixtures/flat/node_modules/@baz/buzz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@baz/buzz", + "version": "1.4.2", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/flat/node_modules/@baz/buzz/server.js b/test/fixtures/flat/node_modules/@baz/buzz/server.js new file mode 100644 index 0000000..09dbd94 --- /dev/null +++ b/test/fixtures/flat/node_modules/@baz/buzz/server.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function bazBuzzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function bazBuzzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/@baz/fizz/index.js b/test/fixtures/flat/node_modules/@baz/fizz/index.js new file mode 100644 index 0000000..4b98ed6 --- /dev/null +++ b/test/fixtures/flat/node_modules/@baz/fizz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function bazFizzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function bazFizzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/@baz/fizz/package.json b/test/fixtures/flat/node_modules/@baz/fizz/package.json new file mode 100644 index 0000000..1fe6f12 --- /dev/null +++ b/test/fixtures/flat/node_modules/@baz/fizz/package.json @@ -0,0 +1,5 @@ +{ + "name": "@baz/fizz", + "version": "1.4.1", + "private": true +} diff --git a/test/fixtures/flat/node_modules/@fu/buzz/index.js b/test/fixtures/flat/node_modules/@fu/buzz/index.js new file mode 100644 index 0000000..f54f71c --- /dev/null +++ b/test/fixtures/flat/node_modules/@fu/buzz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fuBuzzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fuBuzzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/@fu/buzz/package.json b/test/fixtures/flat/node_modules/@fu/buzz/package.json new file mode 100644 index 0000000..97ad959 --- /dev/null +++ b/test/fixtures/flat/node_modules/@fu/buzz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fu/buzz", + "version": "1.3.2", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/flat/node_modules/@fu/fizz/index.js b/test/fixtures/flat/node_modules/@fu/fizz/index.js new file mode 100644 index 0000000..c2d9a82 --- /dev/null +++ b/test/fixtures/flat/node_modules/@fu/fizz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fuFizzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fuFizzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/@fu/fizz/package.json b/test/fixtures/flat/node_modules/@fu/fizz/package.json new file mode 100644 index 0000000..06461db --- /dev/null +++ b/test/fixtures/flat/node_modules/@fu/fizz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fu/fizz", + "version": "1.3.1", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/flat/node_modules/bar/index.js b/test/fixtures/flat/node_modules/bar/index.js new file mode 100644 index 0000000..cc9906b --- /dev/null +++ b/test/fixtures/flat/node_modules/bar/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function barFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function barSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/bar/package.json b/test/fixtures/flat/node_modules/bar/package.json new file mode 100644 index 0000000..041b9ce --- /dev/null +++ b/test/fixtures/flat/node_modules/bar/package.json @@ -0,0 +1,6 @@ +{ + "name": "bar", + "version": "1.2.0", + "main": "./index", + "private": true +} diff --git a/test/fixtures/flat/node_modules/foo/index.js b/test/fixtures/flat/node_modules/foo/index.js new file mode 100644 index 0000000..be3cc72 --- /dev/null +++ b/test/fixtures/flat/node_modules/foo/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fooFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fooSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/flat/node_modules/foo/package.json b/test/fixtures/flat/node_modules/foo/package.json new file mode 100644 index 0000000..63620cc --- /dev/null +++ b/test/fixtures/flat/node_modules/foo/package.json @@ -0,0 +1,6 @@ +{ + "name": "foo", + "version": "1.1.0", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/flat/package.json b/test/fixtures/flat/package.json new file mode 100644 index 0000000..6d64ff5 --- /dev/null +++ b/test/fixtures/flat/package.json @@ -0,0 +1,6 @@ +{ + "name": "flat", + "version": "1.0.0", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/nested/index.js b/test/fixtures/nested/index.js new file mode 100644 index 0000000..4ee51de --- /dev/null +++ b/test/fixtures/nested/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function nestedFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function nestedSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/index.js b/test/fixtures/nested/node_modules/foo/index.js new file mode 100644 index 0000000..be3cc72 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fooFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fooSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/index.js b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/index.js new file mode 100644 index 0000000..c2d9a82 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fuFizzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fuFizzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/package.json b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/package.json new file mode 100644 index 0000000..4ad0088 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@baz/buzz", + "version": "1.4.2", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/server.js b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/server.js new file mode 100644 index 0000000..09dbd94 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/server.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function bazBuzzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function bazBuzzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/package.json b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/package.json new file mode 100644 index 0000000..06461db --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/@fu/fizz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fu/fizz", + "version": "1.3.1", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/index.js b/test/fixtures/nested/node_modules/foo/node_modules/bar/index.js new file mode 100644 index 0000000..cc9906b --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function barFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function barSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/index.js b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/index.js new file mode 100644 index 0000000..4b98ed6 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function bazFizzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function bazFizzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/package.json b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/package.json new file mode 100644 index 0000000..1fe6f12 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz/package.json @@ -0,0 +1,5 @@ +{ + "name": "@baz/fizz", + "version": "1.4.1", + "private": true +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js new file mode 100644 index 0000000..f54f71c --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fuBuzzFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fuBuzzSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/package.json b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/package.json new file mode 100644 index 0000000..97ad959 --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fu/buzz", + "version": "1.3.2", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/nested/node_modules/foo/node_modules/bar/package.json b/test/fixtures/nested/node_modules/foo/node_modules/bar/package.json new file mode 100644 index 0000000..041b9ce --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/node_modules/bar/package.json @@ -0,0 +1,6 @@ +{ + "name": "bar", + "version": "1.2.0", + "main": "./index", + "private": true +} diff --git a/test/fixtures/nested/node_modules/foo/package.json b/test/fixtures/nested/node_modules/foo/package.json new file mode 100644 index 0000000..63620cc --- /dev/null +++ b/test/fixtures/nested/node_modules/foo/package.json @@ -0,0 +1,6 @@ +{ + "name": "foo", + "version": "1.1.0", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/nested/package.json b/test/fixtures/nested/package.json new file mode 100644 index 0000000..ff28ce6 --- /dev/null +++ b/test/fixtures/nested/package.json @@ -0,0 +1,6 @@ +{ + "name": "nested", + "version": "1.0.0", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/single/index.js b/test/fixtures/single/index.js new file mode 100644 index 0000000..c66e13d --- /dev/null +++ b/test/fixtures/single/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function singleFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function singleSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/single/package.json b/test/fixtures/single/package.json new file mode 100644 index 0000000..5a68626 --- /dev/null +++ b/test/fixtures/single/package.json @@ -0,0 +1,6 @@ +{ + "name": "single", + "version": "1.0.0", + "main": "index.js", + "private": true +} diff --git a/test/fixtures/unpackaged-single/index.js b/test/fixtures/unpackaged-single/index.js new file mode 100644 index 0000000..3c51c0d --- /dev/null +++ b/test/fixtures/unpackaged-single/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function unpackagedSingleFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function unpackagedSingleSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/unpackaged/index.js b/test/fixtures/unpackaged/index.js new file mode 100644 index 0000000..759a85d --- /dev/null +++ b/test/fixtures/unpackaged/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function unpackagedFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function unpackagedSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/unpackaged/node_modules/foo/index.js b/test/fixtures/unpackaged/node_modules/foo/index.js new file mode 100644 index 0000000..be3cc72 --- /dev/null +++ b/test/fixtures/unpackaged/node_modules/foo/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function fooFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function fooSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/index.js b/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/index.js new file mode 100644 index 0000000..cc9906b --- /dev/null +++ b/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/index.js @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +module.exports = function barFunction(pacscanPath, options) { + return require(pacscanPath)(options) +} +module.exports.sync = function barSyncFunction(pacscanPath, options) { + return require(pacscanPath).sync(options) +} diff --git a/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/package.json b/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/package.json new file mode 100644 index 0000000..041b9ce --- /dev/null +++ b/test/fixtures/unpackaged/node_modules/foo/node_modules/bar/package.json @@ -0,0 +1,6 @@ +{ + "name": "bar", + "version": "1.2.0", + "main": "./index", + "private": true +} diff --git a/test/fixtures/unpackaged/node_modules/foo/package.json b/test/fixtures/unpackaged/node_modules/foo/package.json new file mode 100644 index 0000000..63620cc --- /dev/null +++ b/test/fixtures/unpackaged/node_modules/foo/package.json @@ -0,0 +1,6 @@ +{ + "name": "foo", + "version": "1.1.0", + "main": "index.js", + "private": true +} diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..b59b237 --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const mkdirp = require('mkdirp') +const ncp = require('ncp').ncp +const path = require('path') +const tmp = require('tmp') + +/** + * The path to the temporary directory created from where fixtures should be run in isolation. + * + * This should only be accessed via {@link getTempDirectory} and will be null until that method is called + * for the first time. + * + * @private + * @type {?string} + */ +let tempDirPath + +/** + * Copies the contents of the fixture directory with the specified name to a temporary directory so that it + * can be run in isolation. + * + * @param {string} name - the name of the fixture whose directory is to be copied + * @return {Promise.} A Promise for retrieving the path to the temporary directory + * containing the fixture contents. + * @public + * @static + */ +exports.copyFixture = function copyFixture(name) { + return new Promise((resolve, reject) => { + const dirPath = exports.getFixtureDirectory(name) + + mkdirp.sync(dirPath) + + ncp(path.join(__dirname, 'fixtures', name), dirPath, (error) => { + if (error) { + reject(error) + } else { + resolve(dirPath) + } + }) + }) +} + +/** + * Creates options to be used by tests. + * + * This is just a convenient method for ensuring that chai and mocha calls are always excluded from knockknock when + * trying to find the caller. + * + * @param {pacscan~Options} [options] - the options to be used (may be null) + * @return {pacscan~Options} The created options. + * @public + * @static + */ +exports.createOptions = function createOptions(options) { + if (!options) { + options = {} + } + + const knockknock = Object.assign({}, options.knockknock) + knockknock.excludes = [ 'chai', 'mocha' ].concat(knockknock.excludes || []) + + options.knockknock = knockknock + + return options +} + +/** + * Returns the path to the temporary directory containing the contents of the fixture directory with the specified + * name. + * + * @param {string} name - the name of the fixture whose temporary directory path is to be returned + * @return {string} The temporary directory path for the fixture with name. + * @public + * @static + */ +exports.getFixtureDirectory = function getFixtureDirectory(name) { + return path.join(exports.getTempDirectory(), 'fixtures', name) +} + +/** + * Returns the path of the temporary directory created from where fixtures should be run in isolation. + * + * @return {string} The temporary directory path. + * @public + * @static + */ +exports.getTempDirectory = function getTempDirectory() { + if (tempDirPath == null) { + tmp.setGracefulCleanup() + + tempDirPath = tmp.dirSync().name + } + + return tempDirPath +} + +/** + * Requires the file at the given path within the directory for the fixture with the specified name. + * + * Since fixture directories are copied to a temporary directory to be run in isolation, they are no longer able to + * easily find and require pacscan by themselves. For this reason, this method returns a proxy to these fixtures so that + * it can pass the absolute path to require pacscan within the fixture while also ensuring that the optons are passed + * through {@link createOptions}. + * + * @param {string} name - the name of the fixture containing the file to be required + * @param {string} filePath - the path of the file (relative to the fixture directory) to be required + * @return {Function} A proxy to be used to call the fixture (also contains a sync method). + * @public + * @static + */ +exports.requireFromFixture = function requireFromFixture(name, filePath) { + const fixture = require(exports.resolveFixtureFile(name, filePath)) + const pacscanPath = path.resolve(__dirname, '../src/pacscan') + + const proxy = function proxy(options) { + return fixture(pacscanPath, exports.createOptions(options)) + } + proxy.sync = function proxySync(options) { + return fixture.sync(pacscanPath, exports.createOptions(options)) + } + + return proxy +} + +/** + * Resolves the specified filePath to the temporary directory for the fixture with the specified + * name. + * + * @param {string} name - the name of the fixture to which filePath is to be resolved + * @param {string} filePath - the path of the file to be resolved + * @return {string} The resolve file path. + * @public + * @static + */ +exports.resolveFixtureFile = function resolveFixtureFile(name, filePath) { + return path.resolve(exports.getFixtureDirectory(name), filePath) +} + +/** + * Resolves all file paths on the specified pkg to the fixtures directory so that they are + * absolute. + * + * @param {pacscan~Package} pkg - the expected package information whose file paths are to be resolved + * @return {pacscan~Package} A reference to the modified pkg. + * @public + * @static + */ +exports.resolvePackageForFixture = function resolvePackageForFixture(pkg) { + const dirPath = path.join(exports.getTempDirectory(), 'fixtures') + + pkg.directory = path.resolve(dirPath, pkg.directory) + if (pkg.main != null) { + pkg.main = path.resolve(dirPath, pkg.main) + } + + return pkg +} diff --git a/test/pacscan.flat.spec.js b/test/pacscan.flat.spec.js new file mode 100644 index 0000000..5ab8901 --- /dev/null +++ b/test/pacscan.flat.spec.js @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const expect = require('chai').expect + +const helpers = require('./helpers') +const pacscan = require('../src/pacscan') + +describe('pacscan:fixture:flat', () => { + before(() => helpers.copyFixture('flat')) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return promise for base package and its dependencies', () => { + const flat = helpers.requireFromFixture('flat', 'index.js') + + return flat() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const flat = helpers.requireFromFixture('flat', 'index.js') + + return flat({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return promise for dependency package only', () => { + const foo = helpers.requireFromFixture('flat', 'node_modules/foo/index.js') + + return foo() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const foo = helpers.requireFromFixture('flat', 'node_modules/foo/index.js') + + return foo({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within scoped dependency package', () => { + it('should return promise for scoped dependency package only', () => { + const fizz = helpers.requireFromFixture('flat', 'node_modules/@fu/fizz/index.js') + + return fizz() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const fizz = helpers.requireFromFixture('flat', 'node_modules/@fu/fizz/index.js') + + return fizz({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return base package and its dependencies', () => { + const flat = helpers.requireFromFixture('flat', 'index.js') + const packages = flat.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const flat = helpers.requireFromFixture('flat', 'index.js') + const packages = flat.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return dependency package only', () => { + const foo = helpers.requireFromFixture('flat', 'node_modules/foo/index.js') + const packages = foo.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const foo = helpers.requireFromFixture('flat', 'node_modules/foo/index.js') + const packages = foo.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and called from within scoped dependency package', () => { + it('should return scoped dependency package only', () => { + const fizz = helpers.requireFromFixture('flat', 'node_modules/@fu/fizz/index.js') + const packages = fizz.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const fizz = helpers.requireFromFixture('flat', 'node_modules/@fu/fizz/index.js') + const packages = fizz.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) +}) diff --git a/test/pacscan.nested.spec.js b/test/pacscan.nested.spec.js new file mode 100644 index 0000000..db99988 --- /dev/null +++ b/test/pacscan.nested.spec.js @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const expect = require('chai').expect + +const helpers = require('./helpers') +const pacscan = require('../src/pacscan') + +describe('pacscan:fixture:nested', () => { + before(() => helpers.copyFixture('nested')) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return promise for base package and its dependencies', () => { + const nested = helpers.requireFromFixture('nested', 'index.js') + + return nested() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const nested = helpers.requireFromFixture('nested', 'index.js') + + return nested({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return promise for dependency package and its dependencies only', () => { + const foo = helpers.requireFromFixture('nested', 'node_modules/foo/index.js') + + return foo() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const foo = helpers.requireFromFixture('nested', 'node_modules/foo/index.js') + + return foo({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within nested dependency package', () => { + it('should return promise for nested dependency package and its dependencies only', () => { + const bar = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/bar/index.js') + + return bar() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const bar = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/bar/index.js') + + return bar({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within scoped dependency package', () => { + it('should return promise for scoped dependency package and its dependencies only', () => { + const fizz = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/@fu/fizz/index.js') + + return fizz() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package and its dependencies', () => { + const fizz = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/@fu/fizz/index.js') + + return fizz({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return base package and its dependencies', () => { + const nested = helpers.requireFromFixture('nested', 'index.js') + const packages = nested.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const nested = helpers.requireFromFixture('nested', 'index.js') + const packages = nested.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return dependency package and its dependencies only', () => { + const foo = helpers.requireFromFixture('nested', 'node_modules/foo/index.js') + const packages = foo.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const foo = helpers.requireFromFixture('nested', 'node_modules/foo/index.js') + const packages = foo.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and called from within nested dependency package', () => { + it('should return nested dependency package and its dependencies only', () => { + const bar = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/bar/index.js') + const packages = bar.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const bar = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/bar/index.js') + const packages = bar.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and called from within scoped dependency package', () => { + it('should return scoped dependency package and its dependencies only', () => { + const fizz = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/@fu/fizz/index.js') + const packages = fizz.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package and its dependencies', () => { + const fizz = helpers.requireFromFixture('nested', 'node_modules/foo/node_modules/@fu/fizz/index.js') + const packages = fizz.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/@fu/fizz', + main: 'nested/node_modules/foo/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz', + main: 'nested/node_modules/foo/node_modules/bar/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo/node_modules/bar', + main: 'nested/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested/node_modules/foo', + main: 'nested/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'nested', + main: 'nested/index.js', + name: 'nested', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) +}) diff --git a/test/pacscan.single.spec.js b/test/pacscan.single.spec.js new file mode 100644 index 0000000..ab5fb0e --- /dev/null +++ b/test/pacscan.single.spec.js @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const expect = require('chai').expect + +const helpers = require('./helpers') +const pacscan = require('../src/pacscan') + +describe('pacscan:fixture:single', () => { + before(() => helpers.copyFixture('single')) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return promise for base package only', () => { + const single = helpers.requireFromFixture('single', 'index.js') + + return single() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'single', + main: 'single/index.js', + name: 'single', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for base package only', () => { + const single = helpers.requireFromFixture('single', 'index.js') + + return single({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'single', + main: 'single/index.js', + name: 'single', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base package', () => { + it('should return base package only', () => { + const single = helpers.requireFromFixture('single', 'index.js') + const packages = single.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'single', + main: 'single/index.js', + name: 'single', + version: '1.0.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return base package only', () => { + const single = helpers.requireFromFixture('single', 'index.js') + const packages = single.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'single', + main: 'single/index.js', + name: 'single', + version: '1.0.0' + }) + ]) + }) + }) + }) + }) +}) diff --git a/test/pacscan.spec.js b/test/pacscan.spec.js index 816c1ec..253ac9b 100644 --- a/test/pacscan.spec.js +++ b/test/pacscan.spec.js @@ -24,10 +24,369 @@ const expect = require('chai').expect +const helpers = require('./helpers') const pacscan = require('../src/pacscan') const version = require('../package.json').version describe('pacscan', () => { + before(() => { + return Promise.all([ + helpers.copyFixture('flat'), + helpers.copyFixture('unpackaged') + ]) + }) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from unknown source without "path"', () => { + it('should return promise rejected', () => { + return pacscan(helpers.createOptions()) + .then(() => { + throw new Error('Expected promise to be rejected') + }) + .catch((error) => { + expect(error.message).to.equal('Could not resolve base directory as file was missing') + }) + }) + }) + + context('and no options are provided', () => { + it('should use default options', () => { + return pacscan() + .then((packages) => { + // mocha modules are only excluded by helpers.createOptions, so will now be included + expect(packages).to.have.length.of.at.least(1) + }) + }) + }) + + context('and "path" targets package directory', () => { + it('should return promise for package and its dependencies', () => { + const dirPath = helpers.getFixtureDirectory('flat') + + return pacscan(helpers.createOptions({ path: dirPath })) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and "path" targets packaged file', () => { + it('should return promise for package and its dependencies', () => { + const filePath = helpers.resolveFixtureFile('flat', 'index.js') + + return pacscan(helpers.createOptions({ path: filePath })) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + }) + + context('and "path" targets unpackaged directory', () => { + it('should return promise for dependencies only', () => { + const dirPath = helpers.getFixtureDirectory('unpackaged') + + return pacscan(helpers.createOptions({ path: dirPath })) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + + context('and "path" targets unpackaged file', () => { + it('should return promise for dependencies only', () => { + const filePath = helpers.resolveFixtureFile('unpackaged', 'index.js') + + return pacscan(helpers.createOptions({ path: filePath })) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from unknown source without "path"', () => { + it('should throw error', () => { + expect(() => { + pacscan.sync(helpers.createOptions()) + }).to.throw(Error, 'Could not resolve base directory as file was missing') + }) + }) + + context('and no options are provided', () => { + it('should use default options', () => { + const packages = pacscan.sync() + + // mocha modules are only excluded by helpers.createOptions, so will now be included + expect(packages).to.have.length.of.at.least(1) + }) + }) + + context('and "path" targets package directory', () => { + it('should return package and its dependencies', () => { + const dirPath = helpers.getFixtureDirectory('flat') + const packages = pacscan.sync(helpers.createOptions({ path: dirPath })) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + + context('and "path" targets packaged file', () => { + it('should return package and its dependencies', () => { + const filePath = helpers.resolveFixtureFile('flat', 'index.js') + const packages = pacscan.sync(helpers.createOptions({ path: filePath })) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/buzz', + main: 'flat/node_modules/@baz/buzz/index.js', + name: '@baz/buzz', + version: '1.4.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@baz/fizz', + main: null, + name: '@baz/fizz', + version: '1.4.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/buzz', + main: 'flat/node_modules/@fu/buzz/index.js', + name: '@fu/buzz', + version: '1.3.2' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/@fu/fizz', + main: 'flat/node_modules/@fu/fizz/index.js', + name: '@fu/fizz', + version: '1.3.1' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/bar', + main: 'flat/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat/node_modules/foo', + main: 'flat/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }), + helpers.resolvePackageForFixture({ + directory: 'flat', + main: 'flat/index.js', + name: 'flat', + version: '1.0.0' + }) + ]) + }) + }) + + context('and "path" targets unpackaged directory', () => { + it('should return dependencies only', () => { + const dirPath = helpers.getFixtureDirectory('unpackaged') + const packages = pacscan.sync(helpers.createOptions({ path: dirPath })) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + + context('and "path" targets unpackaged file', () => { + it('should return dependencies only', () => { + const filePath = helpers.resolveFixtureFile('unpackaged', 'index.js') + const packages = pacscan.sync(helpers.createOptions({ path: filePath })) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + describe('.version', () => { it('should match package version', () => { expect(pacscan.version).to.equal(version) diff --git a/test/pacscan.unpackaged-single.spec.js b/test/pacscan.unpackaged-single.spec.js new file mode 100644 index 0000000..64bc302 --- /dev/null +++ b/test/pacscan.unpackaged-single.spec.js @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const expect = require('chai').expect + +const helpers = require('./helpers') +const pacscan = require('../src/pacscan') + +describe('pacscan:fixture:unpackaged-single', () => { + before(() => helpers.copyFixture('unpackaged-single')) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base directory', () => { + it('should return promise for empty array', () => { + const unpackagedSingle = helpers.requireFromFixture('unpackaged-single', 'index.js') + + return unpackagedSingle() + .then((packages) => { + expect(packages).to.be.empty + }) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for empty array', () => { + const unpackagedSingle = helpers.requireFromFixture('unpackaged-single', 'index.js') + + return unpackagedSingle({ includeParents: true }) + .then((packages) => { + expect(packages).to.be.empty + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base directory', () => { + it('should return empty array', () => { + const unpackagedSingle = helpers.requireFromFixture('unpackaged-single', 'index.js') + const packages = unpackagedSingle.sync() + + expect(packages).to.be.empty + }) + + context('and "includeParents" is enabled', () => { + it('should return empty array', () => { + const unpackagedSingle = helpers.requireFromFixture('unpackaged-single', 'index.js') + const packages = unpackagedSingle.sync({ includeParents: true }) + + expect(packages).to.be.empty + }) + }) + }) + }) +}) diff --git a/test/pacscan.unpackaged.spec.js b/test/pacscan.unpackaged.spec.js new file mode 100644 index 0000000..47da06b --- /dev/null +++ b/test/pacscan.unpackaged.spec.js @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2017 Alasdair Mercer, Skelp + * + * 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. + */ + +'use strict' + +const expect = require('chai').expect + +const helpers = require('./helpers') +const pacscan = require('../src/pacscan') + +describe('pacscan:fixture:unpackaged', () => { + before(() => helpers.copyFixture('unpackaged')) + + context('when asynchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base directory', () => { + it('should return promise for dependencies only', () => { + const unpackaged = helpers.requireFromFixture('unpackaged', 'index.js') + + return unpackaged() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for dependencies only', () => { + const unpackaged = helpers.requireFromFixture('unpackaged', 'index.js') + + return unpackaged({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return promise for dependencies only', () => { + const foo = helpers.requireFromFixture('unpackaged', 'node_modules/foo/index.js') + + return foo() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for dependencies only', () => { + const foo = helpers.requireFromFixture('unpackaged', 'node_modules/foo/index.js') + + return foo({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + }) + + context('and called from within nested dependency package', () => { + it('should return promise for nested dependency package only', () => { + const bar = helpers.requireFromFixture('unpackaged', 'node_modules/foo/node_modules/bar/index.js') + + return bar() + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }) + ]) + }) + }) + + context('and "includeParents" is enabled', () => { + it('should return promise for dependencies only', () => { + const bar = helpers.requireFromFixture('unpackaged', 'node_modules/foo/node_modules/bar/index.js') + + return bar({ includeParents: true }) + .then((packages) => { + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + }) + }) + + context('when synchronous', () => { + before(() => pacscan.clearCache()) + + context('and called from within base directory', () => { + it('should return dependencies only', () => { + const unpackaged = helpers.requireFromFixture('unpackaged', 'index.js') + const packages = unpackaged.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return dependencies only', () => { + const unpackaged = helpers.requireFromFixture('unpackaged', 'index.js') + const packages = unpackaged.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + + context('and called from within dependency package', () => { + it('should return dependencies only', () => { + const foo = helpers.requireFromFixture('unpackaged', 'node_modules/foo/index.js') + const packages = foo.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return dependencies only', () => { + const foo = helpers.requireFromFixture('unpackaged', 'node_modules/foo/index.js') + const packages = foo.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + + context('and called from within nested dependency package', () => { + it('should return nested dependency package only', () => { + const bar = helpers.requireFromFixture('unpackaged', 'node_modules/foo/node_modules/bar/index.js') + const packages = bar.sync() + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }) + ]) + }) + + context('and "includeParents" is enabled', () => { + it('should return dependencies only', () => { + const bar = helpers.requireFromFixture('unpackaged', 'node_modules/foo/node_modules/bar/index.js') + const packages = bar.sync({ includeParents: true }) + + expect(packages).to.eql([ + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo/node_modules/bar', + main: 'unpackaged/node_modules/foo/node_modules/bar/index', + name: 'bar', + version: '1.2.0' + }), + helpers.resolvePackageForFixture({ + directory: 'unpackaged/node_modules/foo', + main: 'unpackaged/node_modules/foo/index.js', + name: 'foo', + version: '1.1.0' + }) + ]) + }) + }) + }) + }) +})