├── .gitignore ├── src ├── npm-dependency-version-checker.js ├── get-project.js ├── version-checker.js ├── utils │ └── single-implementation.js ├── dependency-version-checker.js └── project-wide-dependency-checker.js ├── index.js ├── tests ├── .eslintrc.js ├── lint-tests.js ├── utils │ ├── addon.js │ ├── skip-window.js │ ├── version-checker-bin.js │ ├── project.js │ └── has-a-fixture.js ├── pnp-tests.js ├── yarn-workspaces-test.js ├── index-tests.js └── for-project-tests.js ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── package.json ├── RELEASE.md ├── README.md └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /src/npm-dependency-version-checker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./src/version-checker'); 4 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | mocha: true, 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/lint-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const paths = ['src', 'tests', 'index.js', '.eslintrc.js']; 4 | 5 | require('mocha-eslint')(paths, { 6 | timeout: 10000, 7 | slow: 1000, 8 | }); 9 | -------------------------------------------------------------------------------- /tests/utils/addon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = class Addon extends require('./has-a-fixture') { 4 | constructor(name, version, project, fixture) { 5 | super(name, version, fixture); 6 | this.project = project; 7 | 8 | Object.freeze(this); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /tests/utils/skip-window.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // There are some issues with PNP + workspaces on windows that will require debugging 4 | // https://github.com/ember-cli/ember-cli-version-checker/issues/100 5 | module.exports = process.platform.includes('win32') ? describe.skip : describe; 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['eslint:recommended', 'prettier'], 4 | plugins: ['prettier'], 5 | parserOptions: { 6 | ecmaVersion: 2018, 7 | }, 8 | env: { 9 | node: true, 10 | }, 11 | rules: { 12 | 'prettier/prettier': ['error', { singleQuote: true, trailingComma: 'es5' }], 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/utils/version-checker-bin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function buildVersionCheckerBin(addon) { 4 | return ` 5 | const VersionChecker = require('ember-cli-version-checker'); 6 | 7 | const checker = new VersionChecker(${addon}); 8 | const dep = checker.for(process.argv[2]); 9 | 10 | console.log(process.argv[2] + ': ' + dep.version);`; 11 | }; 12 | -------------------------------------------------------------------------------- /src/get-project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(input) { 4 | if (input.project) { 5 | return input.project; 6 | } else if (input.root && typeof input.isEmberCLIProject === 'function') { 7 | return input; 8 | } else { 9 | throw new Error( 10 | 'You must provide an Addon, EmberApp/EmberAddon, or Project to check dependencies against' 11 | ); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /tests/utils/project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const FixturifyProject = require('fixturify-project'); 4 | module.exports = class Project extends require('./has-a-fixture') { 5 | constructor(name, version) { 6 | super(name, version, new FixturifyProject(name, version)); 7 | this.version = version; 8 | this._addonsInitialized = true; 9 | Object.freeze(this); 10 | } 11 | isEmberCLIProject() {} 12 | }; 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - 'v*' # older version branches 8 | tags: 9 | - '*' 10 | pull_request: {} 11 | schedule: 12 | - cron: '0 6 * * 0' # weekly, on sundays 13 | 14 | jobs: 15 | test: 16 | name: Tests 17 | runs-on: ${{ matrix.os }} 18 | 19 | strategy: 20 | matrix: 21 | node: ['10', '12'] 22 | os: [ubuntu-latest, macOS-latest, windows-latest] 23 | 24 | steps: 25 | - uses: actions/checkout@v1 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: ${{ matrix.node }} 29 | - name: install dependencies 30 | run: yarn 31 | - name: test 32 | run: yarn test 33 | -------------------------------------------------------------------------------- /src/version-checker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const DependencyVersionChecker = require('./dependency-version-checker'); 4 | const ProjectWideDependencyChecker = require('./project-wide-dependency-checker'); 5 | 6 | module.exports = class VersionChecker { 7 | constructor(addon) { 8 | this._addon = addon; 9 | } 10 | 11 | static forProject(project) { 12 | return new ProjectWideDependencyChecker(project); 13 | } 14 | 15 | for(name, type) { 16 | if (type === 'bower') { 17 | throw new Error( 18 | '[ember-cli-version-checker] Bower is no longer supported' 19 | ); 20 | } else { 21 | return new DependencyVersionChecker(this, name); 22 | } 23 | } 24 | 25 | forEmber() { 26 | throw new Error( 27 | `[ember-cli-version-checker] 'checker.forEmber' has been removed, please use 'checker.for(\`ember-source\`)'` 28 | ); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/utils/single-implementation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global WeakMap, Map */ 4 | 5 | const UNIQUE_ADDON_MAP = new WeakMap(); 6 | 7 | /** 8 | * Traverse the project's addons tree to determine singleton root, 9 | * cache the boolean result in project x addonName matrix 10 | */ 11 | module.exports.hasSingleImplementation = hasSingleImplementation; 12 | function hasSingleImplementation(targetName, project) { 13 | if (!UNIQUE_ADDON_MAP.has(project)) { 14 | UNIQUE_ADDON_MAP.set(project, new Map()); 15 | } 16 | let map = UNIQUE_ADDON_MAP.get(project); 17 | if (map.has(targetName)) { 18 | return map.get(targetName); 19 | } 20 | 21 | let lastRoot; 22 | 23 | for (let { name, root } of allAddons(project)) { 24 | if (targetName === name) { 25 | if (lastRoot !== undefined && lastRoot !== root) { 26 | map.set(targetName, false); 27 | return false; 28 | } else { 29 | lastRoot = root; 30 | } 31 | } 32 | } 33 | 34 | map.set(targetName, true); 35 | return true; 36 | } 37 | 38 | module.exports.allAddons = allAddons; 39 | function* allAddons(current) { 40 | if (Array.isArray(current.addons) === false) { 41 | return; 42 | } 43 | 44 | for (let addon of current.addons) { 45 | yield addon; 46 | yield* allAddons(addon); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-version-checker", 3 | "version": "5.1.2", 4 | "description": "Determine if your addon is being used by a minimum version of Ember CLI.", 5 | "homepage": "https://github.com/ember-cli/ember-cli-version-checker", 6 | "bugs": { 7 | "url": "https://github.com/ember-cli/ember-cli-version-checker/issues" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/ember-cli/ember-cli-version-checker.git" 12 | }, 13 | "license": "MIT", 14 | "author": "Robert Jackson ", 15 | "main": "index.js", 16 | "directories": { 17 | "test": "tests" 18 | }, 19 | "files": [ 20 | "src", 21 | "index.js" 22 | ], 23 | "scripts": { 24 | "changelog": "lerna-changelog", 25 | "test": "mocha tests/*-tests.js", 26 | "test:debug": "mocha debug tests/*-tests.js" 27 | }, 28 | "prettier": { 29 | "singleQuote": true, 30 | "trailingComma": "es5" 31 | }, 32 | "dependencies": { 33 | "resolve-package-path": "^3.1.0", 34 | "semver": "^7.3.4", 35 | "silent-error": "^1.1.1" 36 | }, 37 | "devDependencies": { 38 | "eslint-config-prettier": "^6.15.0", 39 | "eslint-plugin-prettier": "^3.4.0", 40 | "fixturify": "^2.1.1", 41 | "fixturify-project": "^2.1.1", 42 | "lerna-changelog": "^1.0.1", 43 | "mocha": "^8.4.0", 44 | "mocha-eslint": "^6.0.0", 45 | "prettier": "^1.19.1", 46 | "release-it": "^14.10.1", 47 | "release-it-lerna-changelog": "^3.1.0" 48 | }, 49 | "engines": { 50 | "node": "10.* || >= 12.*" 51 | }, 52 | "publishConfig": { 53 | "registry": "https://registry.npmjs.org" 54 | }, 55 | "release-it": { 56 | "plugins": { 57 | "release-it-lerna-changelog": { 58 | "infile": "CHANGELOG.md", 59 | "launchEditor": true 60 | } 61 | }, 62 | "git": { 63 | "tagName": "v${version}" 64 | }, 65 | "github": { 66 | "release": true, 67 | "tokenRef": "GITHUB_AUTH" 68 | } 69 | }, 70 | "volta": { 71 | "node": "10.19.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/pnp-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const execSync = require('child_process').execSync; 5 | const Project = require('fixturify-project'); 6 | const skipWindows = require('./utils/skip-window'); 7 | const buildVersionCheckerBin = require('./utils/version-checker-bin'); 8 | 9 | class PnPProject extends Project { 10 | constructor() { 11 | super(...arguments); 12 | this.pkg.private = true; 13 | this.pkg.installConfig = { 14 | pnp: true, 15 | }; 16 | } 17 | 18 | toJSON() { 19 | if (this.name !== 'test-project') { 20 | return {}; 21 | } 22 | 23 | const json = super.toJSON(...arguments); 24 | const own = json[this.name]; 25 | delete own['node_modules']; 26 | return json; 27 | } 28 | } 29 | 30 | skipWindows('with yarn pnp', function() { 31 | this.timeout(600000); 32 | let project; 33 | beforeEach(function() { 34 | project = new PnPProject('test-project'); 35 | 36 | project.addDependency('ember-source-channel-url', '1.1.0'); 37 | project.addDependency('ember-cli-version-checker', `link:${__dirname}/../`); 38 | project.files['index.js'] = buildVersionCheckerBin(`{ 39 | root: process.cwd(), 40 | isEmberCLIProject() {}, 41 | }`); 42 | 43 | project.writeSync(); 44 | 45 | execSync('yarn', { 46 | cwd: project.baseDir, 47 | }); 48 | }); 49 | 50 | afterEach(function() { 51 | project.dispose(); 52 | }); 53 | 54 | it('finds packages that are present', function() { 55 | const result = execSync( 56 | 'node -r ./.pnp.js ./index.js ember-source-channel-url', 57 | { 58 | cwd: project.baseDir, 59 | } 60 | ); 61 | 62 | assert.strictEqual(result.toString(), 'ember-source-channel-url: 1.1.0\n'); 63 | }); 64 | 65 | it('does not find packages that are missing', function() { 66 | const result = execSync('node -r ./.pnp.js ./index.js blah-blah-blah', { 67 | cwd: project.baseDir, 68 | }); 69 | 70 | assert.strictEqual(result.toString(), 'blah-blah-blah: undefined\n'); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /tests/utils/has-a-fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Create a strict "fake" ember-cli addon 4 | class FakeEmberAddon { 5 | constructor(addon) { 6 | this._addon = addon; 7 | Object.freeze(this); 8 | } 9 | 10 | get addons() { 11 | return this._addon.addons; 12 | } 13 | 14 | get name() { 15 | return this._addon.name; 16 | } 17 | 18 | get version() { 19 | return this._addon.version; 20 | } 21 | 22 | get pkg() { 23 | return this._addon._fixture.pkg; 24 | } 25 | 26 | get root() { 27 | return this._addon.root; 28 | } 29 | } 30 | 31 | // abstract 32 | module.exports = class HasAFixture { 33 | constructor(name, version, fixture) { 34 | this._fixture = fixture; 35 | this.name = name; 36 | this.version = version; 37 | this.addons = []; 38 | } 39 | 40 | get root() { 41 | return this._fixture.baseDir; 42 | } 43 | 44 | writeSync(...args) { 45 | return this._fixture.writeSync(...args); 46 | } 47 | 48 | readSync(...args) { 49 | return this._fixture.readSync(...args); 50 | } 51 | 52 | addDependency(...args) { 53 | return this._fixture.addDependency(...args); 54 | } 55 | 56 | addDevDependency(...args) { 57 | return this._fixture.addDevDependency(...args); 58 | } 59 | 60 | addAddon(name, version, cb) { 61 | let addon; 62 | this._fixture.addDependency(name, version, fixture => { 63 | addon = new (require('./addon'))(name, version, this, fixture); 64 | 65 | if (typeof cb === 'function') { 66 | cb(addon); 67 | } 68 | }); 69 | 70 | this.addons.push(new FakeEmberAddon(addon)); 71 | 72 | return addon; 73 | } 74 | 75 | addDevAddon(name, version, cb) { 76 | let addon; 77 | this._fixture.addDevDependency(name, version, fixture => { 78 | addon = new (require('./addon'))(name, version, this, fixture); 79 | if (typeof cb === 'function') { 80 | cb(addon); 81 | } 82 | }); 83 | this.addons.push(new FakeEmberAddon(addon)); 84 | return addon; 85 | } 86 | 87 | dispose() { 88 | this._fixture.dispose(); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | ## Preparation 8 | 9 | Since the majority of the actual release process is automated, the primary 10 | remaining task prior to releasing is confirming that all pull requests that 11 | have been merged since the last release have been labeled with the appropriate 12 | `lerna-changelog` labels and the titles have been updated to ensure they 13 | represent something that would make sense to our users. Some great information 14 | on why this is important can be found at 15 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 16 | guiding principle here is that changelogs are for humans, not machines. 17 | 18 | When reviewing merged PR's the labels to be used are: 19 | 20 | * breaking - Used when the PR is considered a breaking change. 21 | * enhancement - Used when the PR adds a new feature or enhancement. 22 | * bug - Used when the PR fixes a bug included in a previous release. 23 | * documentation - Used when the PR adds or updates documentation. 24 | * internal - Used for internal changes that still require a mention in the 25 | changelog/release notes. 26 | 27 | ## Release 28 | 29 | Once the prep work is completed, the actual release is straight forward: 30 | 31 | * First, ensure that you have installed your projects dependencies: 32 | 33 | ```sh 34 | yarn install 35 | ``` 36 | 37 | * Second, ensure that you have obtained a 38 | [GitHub personal access token][generate-token] with the `repo` scope (no 39 | other permissions are needed). Make sure the token is available as the 40 | `GITHUB_AUTH` environment variable. 41 | 42 | For instance: 43 | 44 | ```bash 45 | export GITHUB_AUTH=abc123def456 46 | ``` 47 | 48 | [generate-token]: https://github.com/settings/tokens/new?scopes=repo&description=GITHUB_AUTH+env+variable 49 | 50 | * And last (but not least 😁) do your release. 51 | 52 | ```sh 53 | npx release-it 54 | ``` 55 | 56 | [release-it](https://github.com/release-it/release-it/) manages the actual 57 | release process. It will prompt you to to choose the version number after which 58 | you will have the chance to hand tweak the changelog to be used (for the 59 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 60 | pushing the tag and commits, etc. 61 | -------------------------------------------------------------------------------- /tests/yarn-workspaces-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const skipWindows = require('./utils/skip-window'); 5 | const buildVersionCheckerBin = require('./utils/version-checker-bin'); 6 | const Project = require('fixturify-project'); 7 | const execSync = require('child_process').execSync; 8 | 9 | skipWindows('with yarn workspace', function() { 10 | this.timeout(600000); 11 | 12 | let project; 13 | beforeEach(function() { 14 | project = new Project('test-project'); 15 | project.workspaces = ['app', 'addon']; 16 | 17 | project.addDependency('bar', '2.0.0'); 18 | project.addDependency('ember-cli-version-checker', `link:${__dirname}/../`); 19 | project.files['index.js'] = buildVersionCheckerBin(`{ 20 | root: process.cwd(), 21 | isEmberCLIProject() {}, 22 | }`); 23 | 24 | project.files.app = { 25 | 'index.js': buildVersionCheckerBin(`{ 26 | root: process.cwd(), 27 | isEmberCLIProject() {}, 28 | }`), 29 | }; 30 | 31 | const theAddon = new Project('the-addon', '0.0.0'); 32 | theAddon.addDependency('bar', `link:${__dirname}/../`); 33 | theAddon.files['dummy.js'] = buildVersionCheckerBin(`{ 34 | project: { 35 | root: '${project.baseDir}/addon', 36 | }, 37 | }`); 38 | 39 | project.writeSync(); 40 | 41 | execSync('yarn', { 42 | cwd: project.baseDir, 43 | }); 44 | 45 | execSync('yarn', { 46 | cwd: project.baseDir + '/addon', 47 | }); 48 | }); 49 | 50 | afterEach(function() { 51 | project.dispose(); 52 | }); 53 | 54 | // https://github.com/ember-cli/ember-cli-version-checker/issues/70 55 | it('uses the sub-package local version over the hoisted version for addon dummy apps', function() { 56 | const result = execSync('node ./dummy.js bar', { 57 | cwd: project.baseDir + '/addon/', 58 | }); 59 | 60 | assert.strictEqual(result.toString(), 'bar: 3.0.0\n'); 61 | }); 62 | 63 | it('uses the hoisted version, if there is no package local version', function() { 64 | const result = execSync('node ./index.js bar', { 65 | cwd: project.baseDir + '/app/', 66 | }); 67 | 68 | assert.strictEqual(result.toString(), 'bar: 2.0.0\n'); 69 | }); 70 | 71 | it('does not find packages that are missing', function() { 72 | const result = execSync('node ./index.js blah-blah-blah', { 73 | cwd: project.baseDir + '/app/', 74 | }); 75 | 76 | assert.strictEqual(result.toString(), 'blah-blah-blah: undefined\n'); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /src/dependency-version-checker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const semver = require('semver'); 3 | const getProject = require('./get-project'); 4 | const resolvePackagePath = require('resolve-package-path'); 5 | 6 | /* 7 | * Retrieve the version field from the package.json file contents. 8 | * NOTE: the callers have already checked that the filePath is not null/undefined. 9 | */ 10 | function getVersionFromJSONFile(filePath) { 11 | try { 12 | // Use the require cache to avoid file I/O after first call on a given path. 13 | let pkg = require(filePath); 14 | 15 | // Note: the callers rely on the fact that pkg.version may be undefined, so 16 | // we must not be "smart" about setting the value to null here to stop any 17 | // repeated calls for the same path. 18 | return pkg.version; 19 | } catch (err) { 20 | // file doesn't exist or is not a file or is not parseable. 21 | return null; 22 | } 23 | } 24 | 25 | /** 26 | * DependencyVersionChecker 27 | */ 28 | class DependencyVersionChecker { 29 | constructor(parent, name) { 30 | this._parent = parent; 31 | this.name = name; 32 | } 33 | 34 | get version() { 35 | if (this._jsonPath === undefined) { 36 | // get the path to the package.json file. resolvePackagePath will 37 | // return the path or null, never undefined, so we can use that 38 | // to only resolvePackagePath once. 39 | let addon = this._parent._addon; 40 | let basedir = addon.root || getProject(addon).root; 41 | this._jsonPath = resolvePackagePath(this.name, basedir); 42 | } 43 | 44 | if (this._version === undefined && this._jsonPath) { 45 | this._version = getVersionFromJSONFile(this._jsonPath); 46 | } 47 | 48 | if (this._version === undefined && this._fallbackJsonPath) { 49 | this._version = getVersionFromJSONFile(this._fallbackJsonPath); 50 | } 51 | 52 | return this._version; 53 | } 54 | 55 | exists() { 56 | return this.version !== undefined; 57 | } 58 | 59 | isAbove(compareVersion) { 60 | if (!this.version) { 61 | return false; 62 | } 63 | return semver.gt(this.version, compareVersion); 64 | } 65 | 66 | assertAbove(compareVersion, _message) { 67 | let message = _message; 68 | if (!this.isAbove(compareVersion)) { 69 | if (!message) { 70 | const parentAddon = this._parent._addon; 71 | message = `The addon \`${parentAddon.name}\` @ \`${parentAddon.root}\` requires the npm package \`${this.name}\` to be above ${compareVersion}, but you have ${this.version}.`; 72 | } 73 | throw new Error(message); 74 | } 75 | } 76 | } 77 | 78 | for (let method of ['gt', 'lt', 'gte', 'lte', 'eq', 'neq', 'satisfies']) { 79 | DependencyVersionChecker.prototype[method] = function(range) { 80 | if (!this.version) { 81 | return method === 'neq'; 82 | } 83 | return semver[method](this.version, range); 84 | }; 85 | } 86 | 87 | module.exports = DependencyVersionChecker; 88 | -------------------------------------------------------------------------------- /src/project-wide-dependency-checker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | hasSingleImplementation, 4 | allAddons, 5 | } = require('./utils/single-implementation'); 6 | const semver = require('semver'); 7 | const SilentError = require('silent-error'); 8 | const { EOL } = require('os'); 9 | /* global Set */ 10 | 11 | module.exports = class ProjectWideDependencyChecker { 12 | constructor(project) { 13 | if ( 14 | !project || 15 | typeof project !== 'object' || 16 | typeof project.isEmberCLIProject !== 'function' 17 | ) { 18 | throw new TypeError( 19 | `[ember-cli-version-checker]'s forProject must be provided an ember-cli project class` 20 | ); 21 | } 22 | 23 | if (project._addonsInitialized !== true) { 24 | throw new TypeError( 25 | `[ember-cli-version-checker]'s forProject must be provided an project instance who's addons have been initialized. This is typically outside the addon's init` 26 | ); 27 | } 28 | 29 | this._project = project; 30 | } 31 | 32 | hasSingleImplementation(name) { 33 | return hasSingleImplementation(name, this._project); 34 | } 35 | 36 | *allAddons() { 37 | yield* allAddons(this._project); 38 | } 39 | 40 | filterAddonsByName(name) { 41 | const addons = []; 42 | 43 | for (let addon of this.allAddons()) { 44 | if (addon.name === name) { 45 | addons.push(addon); 46 | } 47 | } 48 | 49 | return addons; 50 | } 51 | 52 | filterAddonsByNames(names) { 53 | const result = Object.create(null); 54 | for (let name of names) { 55 | result[name] = []; 56 | } 57 | 58 | for (let addon of this.allAddons()) { 59 | const addonResult = result[addon.name]; 60 | if (addonResult !== undefined) { 61 | addonResult.push(addon); 62 | } 63 | } 64 | 65 | return result; 66 | } 67 | 68 | assertSingleImplementation(name, customMessage) { 69 | const uniqueImplementations = new Set(); 70 | 71 | for (let addon of this.allAddons()) { 72 | if (addon.name === name) { 73 | uniqueImplementations.add(addon.root); 74 | } 75 | } 76 | 77 | if (uniqueImplementations.size === 1) { 78 | return true; 79 | } 80 | 81 | let message; 82 | if (uniqueImplementations.size < 1) { 83 | message = `[ember-cli-version-checker] This project requires a single implementation version of the npm package \`${name}\`, but none where found.`; 84 | } else { 85 | if (customMessage) { 86 | message = customMessage; 87 | } else { 88 | message = `[ember-cli-version-checker] This project requires a single implementation version of the npm package \`${name}\`, but there're multiple. Please resolve \`${name}\` to same version:`; 89 | } 90 | } 91 | for (let root of uniqueImplementations) { 92 | message += `\n - ${name} @ ${root}`; 93 | } 94 | 95 | throw new SilentError(message); 96 | } 97 | 98 | check(constraints) { 99 | const names = Object.keys(constraints); 100 | const addons = this.filterAddonsByNames(names); 101 | const node_modules = Object.create(null); 102 | 103 | for (let name in addons) { 104 | const found = addons[name]; 105 | const versions = found.map(addon => addon.pkg.version); 106 | 107 | const constraint = constraints[name]; 108 | const missing = versions.length === 0; 109 | const isSatisfied = 110 | !missing && 111 | versions.every(version => 112 | semver.satisfies(version, constraint, { includePrerelease: true }) 113 | ); 114 | 115 | let message; 116 | if (isSatisfied) { 117 | message = ''; 118 | } else if (missing) { 119 | message = `'${name}' was not found, expected version: [${constraint}]`; 120 | } else { 121 | message = `'${name}' expected version: [${constraint}] but got version${ 122 | versions.length > 1 ? 's' : '' 123 | }: [${versions.join(', ')}]`; 124 | } 125 | 126 | node_modules[name] = { 127 | versions, 128 | isSatisfied, 129 | message, 130 | }; 131 | } 132 | 133 | return new Check(node_modules); 134 | } 135 | }; 136 | 137 | class Check { 138 | constructor(node_modules) { 139 | this.node_modules = node_modules; 140 | Object.freeze(this); 141 | } 142 | 143 | get isSatisfied() { 144 | return Object.values(this.node_modules).every( 145 | node_module => node_module.isSatisfied 146 | ); 147 | } 148 | 149 | get message() { 150 | let result = ''; 151 | 152 | for (const name in this.node_modules) { 153 | const { message } = this.node_modules[name]; 154 | if (message !== '') { 155 | result += ` - ${message}${EOL}`; 156 | } 157 | } 158 | 159 | return result; 160 | } 161 | 162 | assert(description = 'Checker Assertion Failed') { 163 | if (this.isSatisfied) { 164 | return; 165 | } 166 | throw new Error( 167 | `[Ember-cli-version-checker] ${description}\n${this.message}` 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /tests/index-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint-env mocha, node */ 4 | 5 | const assert = require('assert'); 6 | const VersionChecker = require('..'); 7 | 8 | describe('ember-cli-version-checker', function() { 9 | describe('VersionChecker#forEmber', function() { 10 | it('has been removed', function() { 11 | const checker = new VersionChecker({}); 12 | assert.throws( 13 | () => checker.forEmber(), 14 | /'checker.forEmber' has been removed/ 15 | ); 16 | }); 17 | }); 18 | 19 | describe('VersionChecker#for', function() { 20 | let addon, checker, project; 21 | 22 | beforeEach(function() { 23 | const FakeProject = require('./utils/project'); 24 | project = new FakeProject('rsvp', '3.1.4'); 25 | project.addAddon('ember', '2.0.0'); 26 | 27 | addon = project.addAddon('rsvp'); 28 | project.writeSync(); 29 | checker = new VersionChecker(addon); 30 | }); 31 | 32 | describe('nested packages', function() { 33 | it('finds nested packages from the current addons root', function() { 34 | let fakeAddon; 35 | project.addAddon('bar', '3.0.0', addon => { 36 | fakeAddon = addon.addAddon('fake-addon', '3.3.3', addon => { 37 | addon.addAddon('bar', '2.0.0'); 38 | }); 39 | }); 40 | 41 | project.writeSync(); 42 | 43 | assert.equal(new VersionChecker(fakeAddon).for('bar').version, '2.0.0'); 44 | assert.equal(new VersionChecker(addon).for('bar').version, '3.0.0'); 45 | }); 46 | 47 | it('falls back to the project root for instances of `EmberAddon` that do not have a `root` property', function() { 48 | project.addAddon('bar', '3.0.0', addon => { 49 | addon.addAddon('fake-addon', '4.0.0'); 50 | }); 51 | 52 | project.writeSync(); 53 | // silly situation, where dummy app addons may not have a root. 54 | delete addon.root; 55 | 56 | assert.equal(new VersionChecker(addon).for('bar').version, '3.0.0'); 57 | }); 58 | }); 59 | 60 | describe('specified type', function() { 61 | it('defaults to `npm`', function() { 62 | let thing = checker.for('ember'); 63 | 64 | assert.equal(thing.version, '2.0.0'); 65 | }); 66 | 67 | it('throws if `bower` is used, as it is no longer supported', function() { 68 | assert.throws( 69 | () => checker.for('ember', 'bower'), 70 | /Bower is no longer supported/ 71 | ); 72 | }); 73 | }); 74 | 75 | describe('exists', function() { 76 | it('returns true when present', function() { 77 | let thing = checker.for('ember'); 78 | assert.ok(thing.exists()); 79 | }); 80 | 81 | it('returns false when not present', function() { 82 | let thing = checker.for('rando-thing-here'); 83 | 84 | assert.ok(!thing.exists()); 85 | }); 86 | }); 87 | 88 | describe('version', function() { 89 | it('can return a npm version', function() { 90 | let thing = checker.for('ember', 'npm'); 91 | 92 | assert.equal(thing.version, '2.0.0'); 93 | }); 94 | 95 | it('does not exist in nodeModulesPath', function() { 96 | let thing = checker.for('does-not-exist-dummy', 'npm'); 97 | 98 | assert.equal(thing.version, null); 99 | }); 100 | }); 101 | 102 | describe('satisfies', function() { 103 | it('returns true if version is included within range', function() { 104 | let thing = checker.for('ember', 'npm'); 105 | 106 | assert.equal(thing.satisfies('>= 0.0.1'), true); 107 | }); 108 | 109 | it('returns false if version is not included within range', function() { 110 | let thing = checker.for('ember', 'npm'); 111 | 112 | assert.equal(thing.satisfies('>= 99.0.0'), false); 113 | }); 114 | 115 | it('returns false if the dependency does not exist', function() { 116 | let checker = new VersionChecker(addon); 117 | let thing = checker.for('derp', 'npm'); 118 | 119 | assert.equal(thing.satisfies('>= 2.9'), false); 120 | }); 121 | }); 122 | 123 | describe('isAbove', function() { 124 | it('returns true if version is above the specified range', function() { 125 | let thing = checker.for('ember', 'npm'); 126 | 127 | assert.equal(thing.isAbove('0.0.1'), true); 128 | }); 129 | 130 | it('returns false if version is below the specified range', function() { 131 | let thing = checker.for('ember', 'npm'); 132 | 133 | assert.equal(thing.isAbove('99.0.0'), false); 134 | }); 135 | 136 | it('returns false if the dependency does not exist', function() { 137 | let checker = new VersionChecker(addon); 138 | let thing = checker.for('derpy-herpy', 'npm'); 139 | 140 | assert.equal(thing.isAbove('2.9.0'), false); 141 | }); 142 | }); 143 | 144 | describe('gt', function() { 145 | it('returns true if version is above the specified range', function() { 146 | let thing = checker.for('ember', 'npm'); 147 | 148 | assert.equal(thing.gt('0.0.1'), true); 149 | assert.equal(thing.gt('1.9.9'), true); 150 | }); 151 | 152 | it('returns false if version is below the specified range', function() { 153 | let thing = checker.for('ember', 'npm'); 154 | 155 | assert.equal(thing.gt('2.0.0'), false); 156 | assert.equal(thing.gt('99.0.0'), false); 157 | }); 158 | 159 | it('returns false if the dependency does not exist', function() { 160 | let checker = new VersionChecker(addon); 161 | let thing = checker.for('zooeory', 'npm'); 162 | 163 | assert.equal(thing.gt('2.9.0'), false); 164 | }); 165 | }); 166 | 167 | describe('lt', function() { 168 | it('returns false if version is above the specified range', function() { 169 | let thing = checker.for('ember', 'npm'); 170 | 171 | assert.equal(thing.lt('0.0.1'), false); 172 | assert.equal(thing.lt('2.0.0'), false); 173 | }); 174 | 175 | it('returns true if version is below the specified range', function() { 176 | let thing = checker.for('ember', 'npm'); 177 | 178 | assert.equal(thing.lt('2.0.1'), true); 179 | assert.equal(thing.lt('99.0.0'), true); 180 | }); 181 | 182 | it('returns false if the dependency does not exist', function() { 183 | let thing = checker.for('asdfasdf', 'npm'); 184 | 185 | assert.equal(thing.lt('2.9.0'), false); 186 | }); 187 | }); 188 | 189 | describe('gte', function() { 190 | it('returns true if version is above the specified range', function() { 191 | let thing = checker.for('ember', 'npm'); 192 | 193 | assert.equal(thing.gte('0.0.1'), true); 194 | assert.equal(thing.gte('2.0.0'), true); 195 | }); 196 | 197 | it('returns false if version is below the specified range', function() { 198 | let thing = checker.for('ember', 'npm'); 199 | 200 | assert.equal(thing.gte('2.0.1'), false); 201 | assert.equal(thing.gte('99.0.0'), false); 202 | }); 203 | 204 | it('returns false if the dependency does not exist', function() { 205 | let thing = checker.for('hahaha', 'npm'); 206 | 207 | assert.equal(thing.gte('2.9.0'), false); 208 | }); 209 | }); 210 | 211 | describe('lte', function() { 212 | it('returns false if version is above the specified range', function() { 213 | let thing = checker.for('ember', 'npm'); 214 | 215 | assert.equal(thing.lte('0.0.1'), false); 216 | assert.equal(thing.lte('1.9.9'), false); 217 | }); 218 | 219 | it('returns true if version is below the specified range', function() { 220 | let thing = checker.for('ember', 'npm'); 221 | 222 | assert.equal(thing.lte('2.0.0'), true); 223 | assert.equal(thing.lte('99.0.0'), true); 224 | }); 225 | 226 | it('returns false if the dependency does not exist', function() { 227 | let thing = checker.for('lolz', 'npm'); 228 | 229 | assert.equal(thing.lte('2.9.0'), false); 230 | }); 231 | }); 232 | 233 | describe('eq', function() { 234 | it('returns false if version does not match other version', function() { 235 | let thing = checker.for('ember', 'npm'); 236 | 237 | assert.equal(thing.eq('0.0.1'), false); 238 | assert.equal(thing.eq('1.9.9'), false); 239 | assert.equal(thing.eq('2.0.0-beta.1'), false); 240 | assert.equal(thing.eq('2.0.1'), false); 241 | }); 242 | 243 | it('returns true if version matches other version', function() { 244 | let thing = checker.for('ember', 'npm'); 245 | 246 | assert.equal(thing.eq('2.0.0'), true); 247 | }); 248 | 249 | it('returns false if the dependency does not exist', function() { 250 | let thing = checker.for('not-here', 'npm'); 251 | 252 | assert.equal(thing.eq('2.9.0'), false); 253 | }); 254 | }); 255 | 256 | describe('neq', function() { 257 | it('returns true if version does not match other version', function() { 258 | let thing = checker.for('ember', 'npm'); 259 | 260 | assert.equal(thing.neq('0.0.1'), true); 261 | assert.equal(thing.neq('1.9.9'), true); 262 | assert.equal(thing.neq('2.0.0-beta.1'), true); 263 | assert.equal(thing.neq('2.0.1'), true); 264 | }); 265 | 266 | it('returns false if version matches other version', function() { 267 | let thing = checker.for('ember', 'npm'); 268 | 269 | assert.equal(thing.neq('2.0.0'), false); 270 | }); 271 | 272 | it('returns true if the dependency does not exist', function() { 273 | let thing = checker.for('not-here', 'npm'); 274 | 275 | assert.equal(thing.neq('2.9.0'), true); 276 | }); 277 | }); 278 | 279 | describe('assertAbove', function() { 280 | it('throws an error with a default message if a matching version was not found', function() { 281 | let thing = checker.for('ember', 'npm'); 282 | let message = 283 | 'The addon `.*` requires the npm package `ember` to be above 999.0.0, but you have 2.0.0.'; 284 | 285 | assert.throws(() => { 286 | thing.assertAbove('999.0.0'); 287 | }, new RegExp(message)); 288 | }); 289 | 290 | it('throws an error with the given message if a matching version was not found', function() { 291 | let message = 'Must use at least Ember CLI 0.1.2 to use xyz feature'; 292 | let thing = checker.for('ember', 'npm'); 293 | 294 | assert.throws(() => { 295 | thing.assertAbove('999.0.0', message); 296 | }, new RegExp(message)); 297 | }); 298 | }); 299 | }); 300 | }); 301 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember CLI Version Checker 2 | 3 | [![npm version](https://badge.fury.io/js/ember-cli-version-checker.svg)](https://badge.fury.io/js/ember-cli-version-checker) 4 | ![](https://github.com/stefanpenner/do-you-even-test/workflows/ci/badge.svg) 5 | 6 | Makes it easier to determine if a compatible version of a given NPM package is present. 7 | 8 | ## Usage 9 | 10 | Example: 11 | 12 | You want to provide two different sets of templates, based on the currently running Ember version. 13 | 14 | ```javascript 15 | let path = require('path'); 16 | let VersionChecker = require('ember-cli-version-checker'); 17 | 18 | module.exports = { 19 | name: 'awesome-addon', 20 | treeForAddonTemplates(tree) { 21 | let checker = new VersionChecker(this.project); 22 | let dep = checker.for('ember-source'); 23 | 24 | let baseTemplatesPath = path.join(this.root, 'addon/templates'); 25 | 26 | if (dep.satisfies('>= 3.4.0')) { 27 | return this.treeGenerator(path.join(baseTemplatesPath, 'current')); 28 | } else { 29 | return this.treeGenerator(path.join(baseTemplatesPath, 'legacy')); 30 | } 31 | } 32 | }; 33 | ``` 34 | 35 | ## API 36 | 37 | ### Semver Methods (gt, lt, gte, lte, eq, neq, satisfies) 38 | 39 | See https://github.com/npm/node-semver#comparison and https://github.com/npm/node-semver#ranges-1 for more info 40 | 41 | ```js 42 | let VersionChecker = require('ember-cli-version-checker'); 43 | 44 | module.exports = { 45 | name: 'awesome-addon', 46 | init() { 47 | this._super.init.apply(this, arguments); 48 | 49 | let checker = new VersionChecker(this.project); 50 | let dep = checker.for('ember-cli'); 51 | 52 | if (dep.gte('2.0.0')) { 53 | /* deal with 2.0.0+ stuff */ 54 | } else { 55 | /* provide backwards compat */ 56 | }; 57 | } 58 | }; 59 | ``` 60 | 61 | ### assertAbove 62 | 63 | Throws an error with the given message if a minimum version isn't met. 64 | 65 | ```javascript 66 | let VersionChecker = require('ember-cli-version-checker'); 67 | 68 | module.exports = { 69 | name: 'awesome-addon', 70 | init() { 71 | this._super.init.apply(this, arguments); 72 | 73 | let checker = new VersionChecker(this.project); 74 | 75 | checker.for('ember-cli').assertAbove('2.0.0'); 76 | } 77 | }; 78 | ``` 79 | 80 | You can also provide a specific message as the third argument to `assertAbove` if you'd like to customize the output. 81 | 82 | ```javascript 83 | let VersionChecker = require('ember-cli-version-checker'); 84 | 85 | module.exports = { 86 | name: 'awesome-addon', 87 | init() { 88 | this._super.init.apply(this, arguments); 89 | 90 | let checker = new VersionChecker(this.project); 91 | 92 | checker.for('ember-cli').assertAbove('2.0.0', 'To use awesome-addon you must have ember-cli 2.0.0'); 93 | } 94 | }; 95 | ``` 96 | 97 | ### isAbove 98 | 99 | Returns `true` if the packages version is above the specified comparison range. 100 | 101 | ```javascript 102 | let VersionChecker = require('ember-cli-version-checker'); 103 | 104 | module.exports = { 105 | name: 'awesome-addon', 106 | init() { 107 | this._super.init.apply(this, arguments); 108 | 109 | let checker = new VersionChecker(this.project); 110 | let dep = checker.for('ember-cli'); 111 | 112 | if (dep.isAbove('2.0.0')) { 113 | /* deal with 2.0.0 stuff */ 114 | } else { 115 | /* provide backwards compat */ 116 | }; 117 | } 118 | }; 119 | ``` 120 | 121 | ### exists 122 | 123 | Returns `true` or `false` indicating if the dependency exists (at any version). 124 | 125 | ```js 126 | let VersionChecker = require('ember-cli-version-checker'); 127 | 128 | module.exports = { 129 | name: 'awesome-addon', 130 | init() { 131 | this._super.init.apply(this, arguments); 132 | 133 | let checker = new VersionChecker(this.project); 134 | let dep = checker.for('ember-cli-qunit'); 135 | 136 | if (dep.exists()) { 137 | /* do things when present */ 138 | }; 139 | } 140 | }; 141 | ``` 142 | 143 | ### version 144 | 145 | A property that returns the version for the dependency, if the dependency is not found 146 | `undefined` will be returned. 147 | 148 | ```js 149 | let VersionChecker = require('ember-cli-version-checker'); 150 | 151 | module.exports = { 152 | name: 'awesome-addon', 153 | init() { 154 | this._super.init.apply(this, arguments); 155 | 156 | let checker = new VersionChecker(this.project); 157 | let dep = checker.for('ember-cli-qunit'); 158 | 159 | // do something with dep.version 160 | } 161 | }; 162 | ``` 163 | 164 | ### hasSingleImplementation 165 | 166 | Returns `true` if there is only single implementation in node_modules of the 167 | addon. It can either be at app top-level or as a nested dependency. This API 168 | does not work with non-addon npm dependency. 169 | 170 | A unique addon can still be included multiple times if it's a nested 171 | dependency, but they are guaranteed to be resolved to same version in 172 | node_modules. This happens when the dependency in problem specifies a valid 173 | version range or the app uses [yarn 174 | resolutions](https://yarnpkg.com/lang/en/docs/selective-version-resolutions/). 175 | 176 | This is useful if the app wants to make sure there's no unexpected assets from 177 | the addon being included but still allow the addon to be included in the 178 | hierarchy's build process. 179 | 180 | ```js 181 | const VersionChecker = require('ember-cli-version-checker'); 182 | 183 | module.exports = { 184 | name: 'awesome-addon', 185 | included() { 186 | this._super.included.apply(this, arguments); 187 | 188 | let checker = VersionChecker.forProject(this.project); 189 | 190 | if (checker.hasSingleImplementation('')) { 191 | /* do things when is unique */ 192 | } 193 | } 194 | }; 195 | ``` 196 | 197 | ### assertSingleImplementation 198 | 199 | Throws an error if the addon isn't unique, and receives an optional message 200 | param to customize the error message. 201 | 202 | ```js 203 | const VersionChecker = require('ember-cli-version-checker'); 204 | 205 | module.exports = { 206 | name: 'awesome-addon', 207 | included() { 208 | this._super.included.apply(this, arguments); 209 | 210 | let checker = VersionChecker.forProject(this.project); 211 | 212 | checker.assertSingleImplementation('', 'Please make sure has only one implementation, please correct and here is a helpful message!'); 213 | } 214 | }; 215 | ``` 216 | 217 | ### filterAddonsByName 218 | 219 | Find all addon instances with the same name 220 | 221 | ```js 222 | const VersionChecker = require('ember-cli-version-checker'); 223 | 224 | module.exports = { 225 | name: 'awesome-addon', 226 | included() { 227 | this._super.included.apply(this, arguments); 228 | 229 | let checker = VersionChecker.forProject(this.project); 230 | 231 | checker.filterAddonsByName(''); // => an array of addon instances who have the name `` 232 | } 233 | }; 234 | ``` 235 | 236 | 237 | ### allAddons 238 | 239 | An iterator which gives access to all addon instances 240 | 241 | ```js 242 | const VersionChecker = require('ember-cli-version-checker'); 243 | 244 | module.exports = { 245 | name: 'awesome-addon', 246 | included() { 247 | this._super.included.apply(this, arguments); 248 | 249 | let checker = VersionChecker.forProject(this.project); 250 | 251 | for (let { name, root } = checker.allAddons()) { 252 | // access to the addon, in this case name and root 253 | } 254 | } 255 | }; 256 | ``` 257 | 258 | ### check 259 | 260 | A utility to verify that addons are installed at appropriate versions. `npm` 261 | and `yarn` resolve conflicting transitive dependency requirements by installing 262 | multiple versions. They do not include a mechanism for packages to declare 263 | that a dependency must be unique. This is, however, a practical constraint 264 | when building Ember applications (for example, we would not want to build an 265 | application that shipped two versions of Ember Data). [Related discussion on npm](https://github.com/npm/rfcs/pull/23) 266 | 267 | Every addon in the ember ecosystem implicitly depends on `ember-source`, and 268 | most likely a specific version range. If that dependency is specified as a 269 | `package.json` dependency, a mismatch between application and addon would 270 | result in duplicating `ember-source`. Instead of failing the build, we would 271 | build an application with an unknown version of `ember-source`, subverting the 272 | point of specifying dependency version ranges in the first place! The `check` 273 | API provides a mechanism to avoid this and fail fast in the build step, instead 274 | of building an invalid application with harder to debug runtime errors. 275 | 276 | For example, as of today `ember-data` supports `ember-source` `>= 3.4.8`, if it 277 | where to use this addon, it could specify this constraint and provide good 278 | error messages to users. 279 | 280 | ```javascript 281 | const VersionChecker = require('ember-cli-version-checker'); 282 | 283 | module.exports = { 284 | name: 'awesome-addon', 285 | included() { 286 | this._super.included.apply(this, arguments); 287 | 288 | const checker = VersionChecker.forProject(this.project); 289 | const check = checker.check({ 290 | 'ember-source': '>= 3.4.8' 291 | }); 292 | 293 | // if it would like to simply assert 294 | check.assert('[awesome-addon] dependency check failed'); 295 | // will throw an error message similar to the following if the check was not satisfied: 296 | 297 | // [awesome-addon] dependency check failed: 298 | // - 'ember-source' expected version [>= 3.4.8] but got version: [2.0.0] 299 | 300 | // if the requirements are more advanced, we can inspect the resulting check. 301 | 302 | if (!check.isSatisfied) { 303 | const altCheck = checker.check({ 304 | 'magical-polyfil': '>= 1.0.0', 305 | 'ember-source': '>= 3.0.0' 306 | }) 307 | 308 | check.assert('[awesome-addon] dependency check failed:'); 309 | // will throw error message similar to the following if the check was not satisfied: 310 | // [awesome-addon] dependency check failed: 311 | // - 'magical-polyfil' expected version [>= 1.0.0] but got version: [0.0.1] 312 | // - 'ember-source' expected version [>= 3.0.0] but got version: [2.0.-] 313 | } 314 | } 315 | }; 316 | ``` 317 | 318 | ## Note 319 | 320 | ### How does the version resolution works? 321 | 322 | When creating `VersionChecker(addonOrAppOrProject)`, the param needs to have a `root` 323 | property for the VersionChecker to perform node's 324 | [module resolution](https://nodejs.org/api/modules.html#modules_all_together). 325 | 326 | ### Should I use project or parent? 327 | 328 | The two primary options that are valid are: 329 | 330 | - `new VersionChecker(this.project)` 331 | - `new VersionChecker(this.parent)` 332 | 333 | Which one to use depends on if the addon is trying to find a known top-level library or its parent's. 334 | 335 | For example, you may want to check `this.project` root path to find `ember-cli` or `ember-source`, 336 | which are expected to be top-level. 337 | Or you may want to check your parent's specific dependency that affects your addon's behavior, you should create 338 | from `this.parent`. 339 | 340 | If you create via `new VersionChecker(this)` in an addon, it will resolve from your addon's path and have your 341 | own dependency's version instead of top-level dependency's if exists. This will result in unreliable result. 342 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v5.1.2 (2021-01-19) 4 | 5 | #### :rocket: Enhancement 6 | * [#242](https://github.com/ember-cli/ember-cli-version-checker/pull/242) Remove extraneous file I/O when checking the same package version ([@davecombs](https://github.com/davecombs)) 7 | 8 | #### Committers: 2 9 | - Dave Combs ([@davecombs](https://github.com/davecombs)) 10 | - [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) 11 | 12 | 13 | ## v5.1.1 (2020-05-13) 14 | 15 | #### :bug: Bug Fix 16 | * [#207](https://github.com/ember-cli/ember-cli-version-checker/pull/207) fix: Semver constraint matching should allow prereleases ([@runspired](https://github.com/runspired)) 17 | 18 | #### Committers: 1 19 | - Chris Thoburn ([@runspired](https://github.com/runspired)) 20 | 21 | ## v5.1.0 (2020-05-13) 22 | 23 | #### :bug: Bug Fix 24 | * [#207](https://github.com/ember-cli/ember-cli-version-checker/pull/207) fix: Semver constraint matching should allow prereleases ([@runspired](https://github.com/runspired)) 25 | 26 | #### Committers: 1 27 | - Chris Thoburn ([@runspired](https://github.com/runspired)) 28 | 29 | ## v5.1.0 (2020-05-11) 30 | 31 | * [#206](https://github.com/ember-cli/ember-cli-version-checker/pull/206) Add ProjectWideDependencyChecker `check` API([@stefanpenner](https://github.com/stefanpenner)) 32 | 33 | ## v5.0.2 (2020-03-23) 34 | 35 | #### :bug: Bug Fix 36 | * [#178](https://github.com/ember-cli/ember-cli-version-checker/pull/178) Fix hasSingleImplementation ([@xg-wang](https://github.com/xg-wang)) 37 | 38 | #### :house: Internal 39 | * [#179](https://github.com/ember-cli/ember-cli-version-checker/pull/179) Add automated release setup. ([@rwjblue](https://github.com/rwjblue)) 40 | 41 | #### Committers: 2 42 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 43 | - Thomas Wang ([@xg-wang](https://github.com/xg-wang)) 44 | 45 | 46 | ## v5.0.1 (2020-02-14) 47 | 48 | * correct URLs to point to the correct GH organization 49 | 50 | ## v5.0.0 (2020-02-14) 51 | 52 | #### :boom: Breaking Change 53 | * [#165](https://github.com/ember-cli/ember-cli-version-checker/pull/165) Remove Un-needed features ([@stefanpenner](https://github.com/stefanpenner)) 54 | * [#170](https://github.com/ember-cli/ember-cli-version-checker/pull/170) Drop node 8 support ([@stefanpenner](https://github.com/stefanpenner)) 55 | 56 | #### :rocket: Enhancement 57 | * [#173](https://github.com/ember-cli/ember-cli-version-checker/pull/173) Add partial windows coverage ([@stefanpenner](https://github.com/stefanpenner)) 58 | * [#172](https://github.com/ember-cli/ember-cli-version-checker/pull/172) use GH Actions ([@stefanpenner](https://github.com/stefanpenner)) 59 | * [#171](https://github.com/ember-cli/ember-cli-version-checker/pull/171) Improve error message ([@stefanpenner](https://github.com/stefanpenner)) 60 | 61 | #### Committers: 2 62 | - Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) 63 | - [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) 64 | 65 | 66 | ## v4.1.0 (2020-01-17) 67 | 68 | #### :rocket: Enhancement 69 | * [#156](https://github.com/ember-cli/ember-cli-version-checker/pull/156) Add hasSingleImplementation, assertHasSingleImplementation (highlander) ([@xg-wang](https://github.com/xg-wang)) 70 | 71 | #### Committers: 1 72 | - Thomas Wang ([@xg-wang](https://github.com/xg-wang)) 73 | 74 | ## v4.0.1 (2020-01-16) 75 | 76 | #### :rocket: Enhancement 77 | * [#164](https://github.com/ember-cli/ember-cli-version-checker/pull/164) Restore node 8 for a cycle, to ease some upgrades. ([@stefanpenner](https://github.com/stefanpenner)) 78 | 79 | #### Committers: 1 80 | - Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) 81 | 82 | 83 | ## v4.0.0 (2020-01-16) 84 | 85 | #### :boom: Breaking Change & :rocket: Enhancement 86 | * [#163](https://github.com/ember-cli/ember-cli-version-checker/pull/163) drop unsupported node versions and upgrade all dependencies ([@stefanpenner](https://github.com/stefanpenner)) 87 | 88 | #### Committers: 3 89 | - Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) 90 | - Thomas Wang ([@xg-wang](https://github.com/xg-wang)) 91 | - [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) 92 | 93 | ## v3.1.3 (2019-03-26) 94 | 95 | #### :bug: Bug Fix 96 | * [#106](https://github.com/ember-cli/ember-cli-version-checker/pull/106) Fix issue with PnP + missing packages ([@stefanpenner](https://github.com/stefanpenner)) 97 | 98 | #### Committers: 1 99 | - Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) 100 | 101 | ## v3.1.0 (2019-02-28) 102 | 103 | #### :rocket: Enhancement 104 | * [#97](https://github.com/ember-cli/ember-cli-version-checker/pull/97) Perf fix: cache the resolutions identically to how require does. ([@stefanpenner](https://github.com/stefanpenner)) 105 | 106 | #### Committers: 1 107 | - Stefan Penner ([@stefanpenner](https://github.com/stefanpenner)) 108 | 109 | ## v3.0.1 (2019-01-09) 110 | 111 | #### :bug: Bug Fix 112 | * [#71](https://github.com/ember-cli/ember-cli-version-checker/pull/71) fix: fallback to project if addon has no root ([@buschtoens](https://github.com/buschtoens)) 113 | 114 | #### Committers: 1 115 | - Jan Buschtöns ([@buschtoens](https://github.com/buschtoens)) 116 | 117 | 118 | ## v2.2.0 (2019-01-07) 119 | 120 | #### :rocket: Enhancement 121 | * [#83](https://github.com/ember-cli/ember-cli-version-checker/pull/83) Backport Yarn PnP Support to 2.x ([@rwjblue](https://github.com/rwjblue)) 122 | 123 | #### Committers: 1 124 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 125 | 126 | 127 | ## v3.0.0 (2019-01-07) 128 | 129 | #### :boom: Breaking Change 130 | * [#76](https://github.com/ember-cli/ember-cli-version-checker/pull/76) Drop Node 4 support ([@Turbo87](https://github.com/Turbo87)) 131 | 132 | #### :rocket: Enhancement 133 | * [#80](https://github.com/ember-cli/ember-cli-version-checker/pull/80) Support resolution with Yarn PnP. ([@rwjblue](https://github.com/rwjblue)) 134 | 135 | #### :memo: Documentation 136 | * [#64](https://github.com/ember-cli/ember-cli-version-checker/pull/64) Fix code example ([@SergeAstapov](https://github.com/SergeAstapov)) 137 | 138 | #### :house: Internal 139 | * [#82](https://github.com/ember-cli/ember-cli-version-checker/pull/82) chore(ci): install yarn via curl :weary: ([@rwjblue](https://github.com/rwjblue)) 140 | * [#81](https://github.com/ember-cli/ember-cli-version-checker/pull/81) chore(ci): Install latest yarn via apt-get ([@rwjblue](https://github.com/rwjblue)) 141 | 142 | #### Committers: 3 143 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 144 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 145 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 146 | 147 | 148 | ## v2.1.2 (2018-04-27) 149 | 150 | #### :bug: Bug Fix 151 | * [#53](https://github.com/ember-cli/ember-cli-version-checker/pull/53) Ensure `forEmber` _always_ uses the project. ([@rwjblue](https://github.com/rwjblue)) 152 | 153 | #### Committers: 1 154 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 155 | 156 | 157 | ## v2.1.1 (2018-04-27) 158 | 159 | #### :bug: Bug Fix 160 | * [#51](https://github.com/ember-cli/ember-cli-version-checker/pull/51) [bugfix] Allow VersionChecker to work with Projects ([@pzuraq](https://github.com/pzuraq)) 161 | 162 | #### :memo: Documentation 163 | * [#49](https://github.com/ember-cli/ember-cli-version-checker/pull/49) [DOCS]: Document semver methods in README ([@alexander-alvarez](https://github.com/alexander-alvarez)) 164 | 165 | #### :house: Internal 166 | * [#44](https://github.com/ember-cli/ember-cli-version-checker/pull/44) Linting + Prettier ([@rwjblue](https://github.com/rwjblue)) 167 | 168 | #### Committers: 3 169 | - Alex Alvarez ([@alexander-alvarez](https://github.com/alexander-alvarez)) 170 | - Chris Garrett ([@pzuraq](https://github.com/pzuraq)) 171 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 172 | 173 | 174 | ## v2.1.0 (2017-10-08) 175 | 176 | #### :rocket: Enhancement 177 | * [#43](https://github.com/ember-cli/ember-cli-version-checker/pull/43) Add `.exists()` and document `.version`. ([@rwjblue](https://github.com/rwjblue)) 178 | 179 | #### :memo: Documentation 180 | * [#42](https://github.com/ember-cli/ember-cli-version-checker/pull/42) Updates to newer JS syntax ([@twokul](https://github.com/twokul)) 181 | 182 | #### Committers: 2 183 | - Alex Navasardyan ([@twokul](https://github.com/twokul)) 184 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 185 | 186 | 187 | ## v2.0.0 (2017-05-08) 188 | 189 | #### :boom: Breaking Change 190 | * [#41](https://github.com/ember-cli/ember-cli-version-checker/pull/41) Properly handle nested package resolution. ([@rwjblue](https://github.com/rwjblue)) 191 | * [#37](https://github.com/ember-cli/ember-cli-version-checker/pull/37) Refactor to Node 4 supported ES2015 syntax. ([@rwjblue](https://github.com/rwjblue)) 192 | 193 | #### :rocket: Enhancement 194 | * [#39](https://github.com/ember-cli/ember-cli-version-checker/pull/39) Default to `npm` as type. ([@rwjblue](https://github.com/rwjblue)) 195 | * [#36](https://github.com/ember-cli/ember-cli-version-checker/pull/36) CI: Use "auto-dist-tag" for deployment ([@Turbo87](https://github.com/Turbo87)) 196 | 197 | #### Committers: 2 198 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 199 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 200 | 201 | 202 | ## v1.3.0 (2017-04-23) 203 | 204 | #### :rocket: Enhancement 205 | * [#32](https://github.com/ember-cli/ember-cli-version-checker/pull/32) Add support for gte(), lte(), eq() and neq() ([@Turbo87](https://github.com/Turbo87)) 206 | 207 | #### :memo: Documentation 208 | * [#26](https://github.com/ember-cli/ember-cli-version-checker/pull/26) Add forEmber section in readme ([@josemarluedke](https://github.com/josemarluedke)) 209 | 210 | #### :house: Internal 211 | * [#33](https://github.com/ember-cli/ember-cli-version-checker/pull/33) CI: Enable automatic NPM deployment for tags ([@Turbo87](https://github.com/Turbo87)) 212 | * [#31](https://github.com/ember-cli/ember-cli-version-checker/pull/31) Extract classes into seperate files ([@Turbo87](https://github.com/Turbo87)) 213 | 214 | #### Committers: 3 215 | - Jared ([@coderatchet](https://github.com/coderatchet)) 216 | - Josemar Luedke ([@josemarluedke](https://github.com/josemarluedke)) 217 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 218 | 219 | 220 | ## v1.2.0 (2016-12-04) 221 | 222 | #### :rocket: Enhancement 223 | * [#21](https://github.com/ember-cli/ember-cli-version-checker/pull/21) Add #forEmber helper method to get ember version from bower or npm ([@josemarluedke](https://github.com/josemarluedke)) 224 | 225 | #### :bug: Bug Fix 226 | * [#23](https://github.com/ember-cli/ember-cli-version-checker/pull/23) All operations like `gt`, `lt`, `isAbove`, etc must return false if the dependency is not present ([@cibernox](https://github.com/cibernox)) 227 | 228 | #### Committers: 4 229 | - Jacob Jewell ([@jakesjews](https://github.com/jakesjews)) 230 | - Josemar Luedke ([@josemarluedke](https://github.com/josemarluedke)) 231 | - Miguel Camba ([@cibernox](https://github.com/cibernox)) 232 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 233 | 234 | 235 | ## v1.1.7 (2016-10-07) 236 | 237 | #### :bug: Bug Fix 238 | * [#18](https://github.com/ember-cli/ember-cli-version-checker/pull/18) Include only necessary files in the npm package ([@locks](https://github.com/locks)) 239 | 240 | #### Committers: 1 241 | - Ricardo Mendes ([@locks](https://github.com/locks)) 242 | 243 | 244 | ## v1.1.6 (2016-01-20) 245 | 246 | #### :bug: Bug Fix 247 | * [#14](https://github.com/ember-cli/ember-cli-version-checker/pull/14) Fix bower dependency path when in child directory ([@HeroicEric](https://github.com/HeroicEric)) 248 | 249 | #### Committers: 1 250 | - Eric Kelly ([@HeroicEric](https://github.com/HeroicEric)) 251 | 252 | 253 | ## v1.1.5 (2015-12-15) 254 | 255 | #### :bug: Bug Fix 256 | * [#12](https://github.com/ember-cli/ember-cli-version-checker/pull/12) Support beta/canary version number format ([@minichate](https://github.com/minichate)) 257 | 258 | #### Committers: 1 259 | - Christopher Troup ([@minichate](https://github.com/minichate)) 260 | 261 | 262 | ## 1.1.0 (2015-06-18) 263 | 264 | #### :rocket: Enhancement 265 | * [#3](https://github.com/ember-cli/ember-cli-version-checker/pull/3) Allow testing of other packages. ([@rwjblue](https://github.com/rwjblue)) 266 | 267 | #### Committers: 1 268 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 269 | -------------------------------------------------------------------------------- /tests/for-project-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { EOL } = require('os'); 3 | 4 | /* eslint-env mocha, node */ 5 | 6 | const assert = require('assert'); 7 | const VersionChecker = require('..'); 8 | 9 | function isObject(x) { 10 | return typeof x === 'object' && x !== null; 11 | } 12 | 13 | describe('ember-cli-version-checker', function() { 14 | describe('VersionChecker.forProject', function() { 15 | it('errors if given a project without initialized addons', function() { 16 | assert.throws(() => { 17 | VersionChecker.forProject(); 18 | }, /forProject must be provided an ember-cli project class/); 19 | 20 | assert.throws(() => { 21 | VersionChecker.forProject(null); 22 | }, /forProject must be provided an ember-cli project class/); 23 | 24 | assert.throws(() => { 25 | VersionChecker.forProject({}); 26 | }, /forProject must be provided an ember-cli project class/); 27 | 28 | assert.throws(() => { 29 | VersionChecker.forProject({ isEmberCLIProject() {} }); 30 | }, /forProject must be provided an project instance who's addons have been initialized./); 31 | 32 | VersionChecker.forProject({ 33 | isEmberCLIProject() {}, 34 | _addonsInitialized: true, 35 | }); 36 | }); 37 | 38 | let project; 39 | beforeEach(function() { 40 | const FakeProject = require('./utils/project'); 41 | project = new FakeProject('rsvp', '3.1.4'); 42 | project.addAddon('ember', '2.0.0'); 43 | project.writeSync(); 44 | }); 45 | 46 | describe('Multiple instances check', function() { 47 | let checker; 48 | 49 | beforeEach(function() { 50 | const FakeProject = require('./utils/project'); 51 | project = new FakeProject('rsvp', '3.1.4'); 52 | project.addAddon('ember', '2.0.0'); 53 | 54 | project.writeSync(); 55 | checker = VersionChecker.forProject(project); 56 | 57 | project.addAddon('alpha', '1.1.0-alpha.1'); 58 | project.addAddon('beta', '1.1.0-beta.1'); 59 | project.addAddon('trailing-alpha', '1.0.0-alpha.1'); 60 | project.addAddon('top', '1.0.0'); 61 | project.addAddon('bar', '3.0.0'); 62 | project.addAddon('fake-addon', '3.0.0', addon => { 63 | addon.addAddon('foo', '1.0.0'); 64 | addon.addAddon('bar', '2.0.0', addon => { 65 | addon.addAddon('foo', '1.0.0'); 66 | }); 67 | }); 68 | }); 69 | 70 | it('validates VersionChecker.forProject throws unless given a project', function() { 71 | assert.throws(() => { 72 | VersionChecker.forProject({}); 73 | }, /forProject must be provided an ember-cli project class/); 74 | }); 75 | 76 | it('#assertSingleImplementation throws correctly', function() { 77 | assert.throws(() => { 78 | checker.assertSingleImplementation('--no-such-addon--'); 79 | }, /This project requires a single implementation version of the npm package `--no-such-addon--`, but none where found./); 80 | assert.throws(() => { 81 | checker.assertSingleImplementation('bar'); 82 | }, /This project requires a single implementation version of the npm package `bar`, but there're multiple. Please resolve `bar` to same version./); 83 | assert.throws(() => { 84 | checker.assertSingleImplementation('bar'); 85 | }, / - bar @ .*rsvp.*node_modules.*fake-addon.*node_modules.*bar/); 86 | assert.throws(() => { 87 | checker.assertSingleImplementation('bar'); 88 | }, / - bar @ .*rsvp.*node_modules.*bar/); 89 | }); 90 | 91 | it('#hasSingleImplementation detects singleton', function() { 92 | assert.equal(checker.hasSingleImplementation('foo'), false); 93 | assert.equal(checker.hasSingleImplementation('top'), true); 94 | }); 95 | 96 | it('has a working #filterAddonsByName', () => { 97 | assert.equal(checker.filterAddonsByName('foo').length, 2); 98 | assert.equal(checker.filterAddonsByName('top').length, 1); 99 | assert.equal(checker.filterAddonsByName('bar').length, 2); 100 | assert.equal(checker.filterAddonsByName('never-ever-ever').length, 0); 101 | assert.equal( 102 | checker.filterAddonsByName('bar').filter(isObject).length, 103 | 2 104 | ); 105 | }); 106 | 107 | it('has a working #filterAddonsByNames', () => { 108 | const result = checker.filterAddonsByNames([ 109 | 'foo', 110 | 'top', 111 | 'never-ever-ever', 112 | ]); 113 | 114 | assert.deepEqual(Object.keys(result), [ 115 | 'foo', 116 | 'top', 117 | 'never-ever-ever', 118 | ]); 119 | 120 | assert.deepEqual( 121 | result.foo.map(x => x.name), 122 | ['foo', 'foo'] 123 | ); 124 | 125 | assert.deepEqual( 126 | result.top.map(x => x.name), 127 | ['top'] 128 | ); 129 | 130 | assert.deepEqual( 131 | result['never-ever-ever'].map(x => x.name), 132 | [] 133 | ); 134 | }); 135 | 136 | it('#hasSingleImplementation finds duplication and can be fixed by resolution', function() { 137 | assert.ok(!checker.hasSingleImplementation('bar')); 138 | }); 139 | 140 | it('has a functioning allAddons iterator', function() { 141 | assert.deepEqual( 142 | [...checker.allAddons()].map(x => x.name), 143 | [ 144 | 'ember', 145 | 'alpha', 146 | 'beta', 147 | 'trailing-alpha', 148 | 'top', 149 | 'bar', 150 | 'fake-addon', 151 | 'foo', 152 | 'bar', 153 | 'foo', 154 | ] 155 | ); 156 | }); 157 | 158 | describe('#check', function() { 159 | it('noop works as expected', function() { 160 | const checked = checker.check({}); 161 | assert.deepEqual(checked.node_modules, {}); 162 | assert.equal(checked.isSatisfied, true); 163 | assert.equal(checked.message, ''); 164 | }); 165 | 166 | it('is satisfied by pre-releases', function() { 167 | const checked = checker.check({ 168 | alpha: '>= 1.0.0', 169 | beta: '>= 1.0.0', 170 | }); 171 | assert.deepEqual(checked.node_modules, { 172 | alpha: { 173 | versions: ['1.1.0-alpha.1'], 174 | isSatisfied: true, 175 | message: ``, 176 | }, 177 | beta: { 178 | versions: ['1.1.0-beta.1'], 179 | isSatisfied: true, 180 | message: ``, 181 | }, 182 | }); 183 | assert.equal(checked.isSatisfied, true); 184 | }); 185 | 186 | it('is not satisfied by pre-releases in the same minor', function() { 187 | const checked = checker.check({ 188 | 'trailing-alpha': '>= 1.0.0', 189 | }); 190 | assert.deepEqual(checked.node_modules, { 191 | 'trailing-alpha': { 192 | versions: ['1.0.0-alpha.1'], 193 | isSatisfied: false, 194 | message: `'trailing-alpha' expected version: [>= 1.0.0] but got version: [1.0.0-alpha.1]`, 195 | }, 196 | }); 197 | assert.equal(checked.isSatisfied, false); 198 | }); 199 | 200 | it('is not satisfied if checked deps are missing', function() { 201 | const checked = checker.check({ 202 | '@ember-cli/no-such-addon--': '*', 203 | '@ember-cli/no-such-other-addon--': '*', 204 | }); 205 | assert.deepEqual(checked.node_modules, { 206 | '@ember-cli/no-such-addon--': { 207 | versions: [], 208 | isSatisfied: false, 209 | message: `'@ember-cli/no-such-addon--' was not found, expected version: [*]`, 210 | }, 211 | '@ember-cli/no-such-other-addon--': { 212 | versions: [], 213 | isSatisfied: false, 214 | message: `'@ember-cli/no-such-other-addon--' was not found, expected version: [*]`, 215 | }, 216 | }); 217 | assert.equal(checked.isSatisfied, false); 218 | }); 219 | 220 | it('is not satisfied if checked deps are present but the versions are not satisfied', function() { 221 | const checked = checker.check({ 222 | top: '2.0.0', 223 | bar: '4.0.0', 224 | }); 225 | assert.deepEqual(checked.node_modules, { 226 | top: { 227 | versions: ['1.0.0'], 228 | isSatisfied: false, 229 | message: `'top' expected version: [2.0.0] but got version: [1.0.0]`, 230 | }, 231 | bar: { 232 | versions: ['3.0.0', '2.0.0'], 233 | isSatisfied: false, 234 | message: `'bar' expected version: [4.0.0] but got versions: [3.0.0, 2.0.0]`, 235 | }, 236 | }); 237 | assert.equal(checked.isSatisfied, false); 238 | }); 239 | 240 | it('is not satisfied if checked deps are present but not all versions are not satisfied', function() { 241 | const checked = checker.check({ 242 | top: '2.0.0', 243 | bar: '4.0.0', 244 | }); 245 | assert.deepEqual(checked.node_modules, { 246 | top: { 247 | versions: ['1.0.0'], 248 | isSatisfied: false, 249 | message: `'top' expected version: [2.0.0] but got version: [1.0.0]`, 250 | }, 251 | bar: { 252 | versions: ['3.0.0', '2.0.0'], 253 | isSatisfied: false, 254 | message: `'bar' expected version: [4.0.0] but got versions: [3.0.0, 2.0.0]`, 255 | }, 256 | }); 257 | assert.equal(checked.isSatisfied, false); 258 | }); 259 | 260 | it('is satisfied if all checked deps are present and the versions are satisfied', function() { 261 | const checked = checker.check({ 262 | top: '1.0.0', 263 | bar: '>= 2.0.0', 264 | }); 265 | 266 | assert.deepEqual(checked.node_modules, { 267 | top: { 268 | versions: ['1.0.0'], 269 | isSatisfied: true, 270 | message: '', 271 | }, 272 | bar: { 273 | versions: ['3.0.0', '2.0.0'], 274 | isSatisfied: true, 275 | message: '', 276 | }, 277 | }); 278 | assert.equal(checked.isSatisfied, true); 279 | assert.equal(checked.message, ''); 280 | }); 281 | 282 | it('is NOT satisfied with partial match', function() { 283 | const checked = checker.check({ 284 | top: '1.0.0', 285 | bar: '>= 2.0.1', 286 | }); 287 | 288 | assert.deepEqual(checked.node_modules, { 289 | top: { 290 | versions: ['1.0.0'], 291 | isSatisfied: true, 292 | message: '', 293 | }, 294 | bar: { 295 | versions: ['3.0.0', '2.0.0'], 296 | isSatisfied: false, 297 | message: `'bar' expected version: [>= 2.0.1] but got versions: [3.0.0, 2.0.0]`, 298 | }, 299 | }); 300 | assert.equal(checked.isSatisfied, false); 301 | }); 302 | 303 | it('is not satisfied if some checked deps are missing', function() { 304 | const checked = checker.check({ 305 | '@ember-cli/no-such-addon--': '*', 306 | ember: '*', 307 | }); 308 | assert.deepEqual(checked.node_modules, { 309 | '@ember-cli/no-such-addon--': { 310 | versions: [], 311 | isSatisfied: false, 312 | message: `'@ember-cli/no-such-addon--' was not found, expected version: [*]`, 313 | }, 314 | ember: { 315 | versions: ['2.0.0'], 316 | isSatisfied: true, 317 | message: '', 318 | }, 319 | }); 320 | assert.equal(checked.isSatisfied, false); 321 | }); 322 | 323 | it('checked.message is a an ok default error message', function() { 324 | const checked = checker.check({ 325 | '@ember-cli/no-such-addon--': '*', 326 | ember: '*', 327 | }); 328 | const message = ` - '@ember-cli/no-such-addon--' was not found, expected version: [*]${EOL}`; 329 | assert.equal(checked.message, message); 330 | }); 331 | 332 | it('checker has a functioning assert method', function() { 333 | checker.check({}).assert(); 334 | 335 | assert.throws(() => { 336 | checker 337 | .check({ '@ember-cli/no-such-addon--': '*', ember: '*' }) 338 | .assert(); 339 | }, /Checker Assertion Failed/); 340 | 341 | assert.throws(() => { 342 | checker 343 | .check({ '@ember-cli/no-such-addon--': '*', ember: '*' }) 344 | .assert(); 345 | }, /- '@ember-cli\/no-such-addon--' was not found, expected version: \[\*\]/); 346 | 347 | assert.throws(() => { 348 | checker 349 | .check({ '@ember-cli/no-such-addon--': '*', ember: '*' }) 350 | .assert('custom description'); 351 | }, /custom description/); 352 | }); 353 | }); 354 | }); 355 | }); 356 | }); 357 | --------------------------------------------------------------------------------