From 0b9622deddf52075cfade352c56b26a69d23675f Mon Sep 17 00:00:00 2001 From: Dominik Ferber Date: Tue, 13 Oct 2015 18:42:31 +0200 Subject: [PATCH] fix(internal): Add locus resloving capabilities Resolve all Meteor.isClient, Meteor.isServer and Meteor.isCordova LogicalExpressions --- lib/index.js | 2 +- lib/rules/audit-argument-checks.js | 8 +- lib/rules/core.js | 11 +- lib/rules/no-blaze-lifecycle-assignment.js | 8 +- lib/rules/no-session.js | 9 +- lib/rules/no-zero-timeout.js | 8 +- lib/rules/pubsub.js | 61 ++---- lib/util/ast/index.js | 1 - lib/util/ast/isInBlock.js | 44 ---- .../executors/filterExecutorsByAncestors.js | 33 +++ lib/util/executors/getExecutors.js | 10 + lib/util/executors/getExecutorsByEnv.js | 51 +++++ lib/util/executors/getExecutorsFromTest.js | 28 +++ lib/util/executors/invert.js | 5 + lib/util/executors/isMeteorBlockOnlyTest.js | 28 +++ lib/util/executors/sets.js | 26 +++ lib/util/getMeta.js | 6 - lib/util/index.js | 2 + lib/util/{internal => meta}/findOneUpwards.js | 0 lib/util/meta/getMeta.js | 6 + lib/util/{internal => meta}/getMeteorMeta.js | 9 +- .../{internal => meta}/getRelativePath.js | 0 lib/util/{internal => meta}/getRootPath.js | 0 lib/util/meta/isLintedEnv.js | 5 + .../{internal => meta}/isMeteorProject.js | 0 .../{internal => meta}/stripPathPrefix.js | 0 package.json | 3 +- scripts/new-rule.js | 18 +- tests/lib/rules/audit-argument-checks.js | 9 +- tests/lib/rules/core.js | 18 +- .../rules/no-blaze-lifecycle-assignment.js | 9 +- tests/lib/rules/no-session.js | 48 +--- tests/lib/rules/no-zero-timeout.js | 9 +- tests/lib/rules/pubsub.js | 85 ++------ tests/lib/util/ast/isInBlock.js | 25 --- .../executors/filterExecutorsByAncestors.js | 190 ++++++++++++++++ tests/lib/util/executors/getExecutors.js | 15 ++ tests/lib/util/executors/getExecutorsByEnv.js | 80 +++++++ .../util/executors/getExecutorsFromTest.js | 149 +++++++++++++ .../util/executors/isMeteorBlockOnlyTest.js | 206 ++++++++++++++++++ tests/lib/util/executors/sets.js | 39 ++++ .../util/{internal => meta}/findOneUpwards.js | 2 +- tests/lib/util/{ => meta}/getMeta.js | 4 +- .../util/{internal => meta}/getMeteorMeta.js | 2 +- .../{internal => meta}/getRelativePath.js | 2 +- .../util/{internal => meta}/getRootPath.js | 2 +- .../{internal => meta}/isMeteorProject.js | 2 +- .../{internal => meta}/stripPathPrefix.js | 2 +- 48 files changed, 1004 insertions(+), 276 deletions(-) delete mode 100644 lib/util/ast/isInBlock.js create mode 100644 lib/util/executors/filterExecutorsByAncestors.js create mode 100644 lib/util/executors/getExecutors.js create mode 100644 lib/util/executors/getExecutorsByEnv.js create mode 100644 lib/util/executors/getExecutorsFromTest.js create mode 100644 lib/util/executors/invert.js create mode 100644 lib/util/executors/isMeteorBlockOnlyTest.js create mode 100644 lib/util/executors/sets.js delete mode 100644 lib/util/getMeta.js create mode 100644 lib/util/index.js rename lib/util/{internal => meta}/findOneUpwards.js (100%) create mode 100644 lib/util/meta/getMeta.js rename lib/util/{internal => meta}/getMeteorMeta.js (93%) rename lib/util/{internal => meta}/getRelativePath.js (100%) rename lib/util/{internal => meta}/getRootPath.js (100%) create mode 100644 lib/util/meta/isLintedEnv.js rename lib/util/{internal => meta}/isMeteorProject.js (100%) rename lib/util/{internal => meta}/stripPathPrefix.js (100%) delete mode 100644 tests/lib/util/ast/isInBlock.js create mode 100644 tests/lib/util/executors/filterExecutorsByAncestors.js create mode 100644 tests/lib/util/executors/getExecutors.js create mode 100644 tests/lib/util/executors/getExecutorsByEnv.js create mode 100644 tests/lib/util/executors/getExecutorsFromTest.js create mode 100644 tests/lib/util/executors/isMeteorBlockOnlyTest.js create mode 100644 tests/lib/util/executors/sets.js rename tests/lib/util/{internal => meta}/findOneUpwards.js (92%) rename tests/lib/util/{ => meta}/getMeta.js (81%) rename tests/lib/util/{internal => meta}/getMeteorMeta.js (98%) rename tests/lib/util/{internal => meta}/getRelativePath.js (88%) rename tests/lib/util/{internal => meta}/getRootPath.js (91%) rename tests/lib/util/{internal => meta}/isMeteorProject.js (87%) rename tests/lib/util/{internal => meta}/stripPathPrefix.js (81%) diff --git a/lib/index.js b/lib/index.js index 1278d541e1..56fc6e3d78 100755 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,4 @@ -import getMeta from './util/getMeta' +import {getMeta} from './util' function unpack (rule) { const packedRule = require(rule) diff --git a/lib/rules/audit-argument-checks.js b/lib/rules/audit-argument-checks.js index a73eda11b8..ec89a0b564 100644 --- a/lib/rules/audit-argument-checks.js +++ b/lib/rules/audit-argument-checks.js @@ -10,7 +10,9 @@ import {isMeteorCall, isFunction} from '../util/ast' // ----------------------------------------------------------------------------- -module.exports = () => (context) => { +module.exports = getMeta => context => { + + const {isLintedEnv} = getMeta(context.getFilename()) // --------------------------------------------------------------------------- // Helpers @@ -57,6 +59,10 @@ module.exports = () => (context) => { // Public // --------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { CallExpression: (node) => { diff --git a/lib/rules/core.js b/lib/rules/core.js index 9a7971cc68..32be25b9a3 100644 --- a/lib/rules/core.js +++ b/lib/rules/core.js @@ -9,16 +9,11 @@ // Rule Definition // ----------------------------------------------------------------------------- -import {CLIENT, SERVER, UNIVERSAL} from '../util/environment' import {isMeteorProp, isMeteorCall} from '../util/ast' module.exports = getMeta => context => { - const {env} = getMeta(context.getFilename()) - - if (env !== CLIENT && env !== SERVER && env !== UNIVERSAL) { - return {} - } + const {isLintedEnv} = getMeta(context.getFilename()) // --------------------------------------------------------------------------- // Helpers @@ -43,6 +38,10 @@ module.exports = getMeta => context => { // Public // --------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { AssignmentExpression: function (node) { diff --git a/lib/rules/no-blaze-lifecycle-assignment.js b/lib/rules/no-blaze-lifecycle-assignment.js index 3eaf271bce..142822eab2 100644 --- a/lib/rules/no-blaze-lifecycle-assignment.js +++ b/lib/rules/no-blaze-lifecycle-assignment.js @@ -7,7 +7,9 @@ // Rule Definition // ----------------------------------------------------------------------------- -module.exports = () => (context) => { +module.exports = getMeta => context => { + + const {isLintedEnv} = getMeta(context.getFilename()) // --------------------------------------------------------------------------- // Helpers @@ -41,6 +43,10 @@ module.exports = () => (context) => { // Public // --------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { AssignmentExpression: (node) => { diff --git a/lib/rules/no-session.js b/lib/rules/no-session.js index b5c6498703..2d42c911c0 100644 --- a/lib/rules/no-session.js +++ b/lib/rules/no-session.js @@ -6,18 +6,23 @@ // ----------------------------------------------------------------------------- // Rule Definition // ----------------------------------------------------------------------------- +module.exports = getMeta => context => { -module.exports = () => context => { + const {isLintedEnv} = getMeta(context.getFilename()) // ------------------------------------------------------------------------- // Public // ------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { MemberExpression (node) { if (node.object.name === 'Session') { - context.report(node, 'Unexpected Session statement.') + context.report(node, 'Unexpected Session statement') } } diff --git a/lib/rules/no-zero-timeout.js b/lib/rules/no-zero-timeout.js index a400397841..f37f27ca00 100644 --- a/lib/rules/no-zero-timeout.js +++ b/lib/rules/no-zero-timeout.js @@ -9,12 +9,18 @@ import {isMeteorCall} from '../util/ast' // Rule Definition // ----------------------------------------------------------------------------- -module.exports = () => (context) => { +module.exports = getMeta => context => { + + const {isLintedEnv} = getMeta(context.getFilename()) // ------------------------------------------------------------------------- // Public // ------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { CallExpression: function (node) { diff --git a/lib/rules/pubsub.js b/lib/rules/pubsub.js index c6768ca723..87057dff63 100644 --- a/lib/rules/pubsub.js +++ b/lib/rules/pubsub.js @@ -5,19 +5,16 @@ * See LICENSE file in root directory for full license. */ -import {CLIENT, SERVER, UNIVERSAL} from '../util/environment' -import {isMeteorCall, isInServerBlock, isInClientBlock} from '../util/ast' +import {isMeteorCall} from '../util/ast' +import {getExecutors} from '../util' // ----------------------------------------------------------------------------- // Rule Definition // ----------------------------------------------------------------------------- module.exports = getMeta => context => { - const {env} = getMeta(context.getFilename()) - if (env !== CLIENT && env !== SERVER && env !== UNIVERSAL) { - return {} - } + const {isLintedEnv, env} = getMeta(context.getFilename()) // ------------------------------------------------------------------------- // Helpers @@ -43,62 +40,50 @@ module.exports = getMeta => context => { context.report(node, 'Allowed on client only') } - function checkMeteorPublish (node, ancestors) { + function checkMeteorPublish (node, executors) { if (!isMeteorCall(node, 'publish')) { return } - switch (env) { - case CLIENT: - noPublishOnClient(node) - break - case SERVER: - expectTwoArguments(node) - break - case UNIVERSAL: - if (isInServerBlock(ancestors)) { - expectTwoArguments(node) - } else { - noPublishOnClient(node) - } - break + if (executors.has('browser')) { + noPublishOnClient(node) + } + + if (executors.has('server')) { + expectTwoArguments(node) } } - function checkMeteorSubscribe (node, ancestors) { + function checkMeteorSubscribe (node, executors) { if (!isMeteorCall(node, 'subscribe')) { return } - switch (env) { - case CLIENT: - atLeastOneArgument(node) - break - case SERVER: - noSubscribeOnServer(node) - break - case UNIVERSAL: - if (isInClientBlock(ancestors)) { - atLeastOneArgument(node) - } else { - noSubscribeOnServer(node) - } - break + if (executors.has('browser')) { + atLeastOneArgument(node) + } + if (executors.has('server')) { + noSubscribeOnServer(node) } } // ------------------------------------------------------------------------- // Public // ------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { CallExpression: function (node) { const ancestors = context.getAncestors() + const executors = getExecutors(env, ancestors) // Meteor.publish - checkMeteorPublish(node, ancestors) - checkMeteorSubscribe(node, ancestors) + checkMeteorPublish(node, executors) + checkMeteorSubscribe(node, executors) } } diff --git a/lib/util/ast/index.js b/lib/util/ast/index.js index 16d202784a..665cdd5ec0 100644 --- a/lib/util/ast/index.js +++ b/lib/util/ast/index.js @@ -1,4 +1,3 @@ export {default as isMeteorCall} from './isMeteorCall' export {default as isMeteorProp} from './isMeteorProp' -export {default as isInBlock, isInClientBlock, isInServerBlock} from './isInBlock' export {default as isFunction} from './isFunction' diff --git a/lib/util/ast/isInBlock.js b/lib/util/ast/isInBlock.js deleted file mode 100644 index e06658eadb..0000000000 --- a/lib/util/ast/isInBlock.js +++ /dev/null @@ -1,44 +0,0 @@ -import {CLIENT, SERVER} from '../environment' -import isMeteorProp from './isMeteorProp' -import invariant from 'invariant' - -export function isInBlock (ancestors, env) { - - invariant(Array.isArray(ancestors), 'isInBlock: ancestors is not an array') - invariant(!!env, 'isInBlock: called without environment') - invariant(env === CLIENT || env === SERVER, 'isInBlock: unkown environment') - - if (ancestors.length === 0) { - return false - } - - let isInServer = false - let isInClient = false - for (let i = 0; i < ancestors.length; i++) { - const ancestor = ancestors[i] - - if (ancestor.type === 'IfStatement' && ancestor.test.type === 'MemberExpression') { - if (isMeteorProp(ancestor.test, 'isServer')) { - isInServer = true - } - if (isMeteorProp(ancestor.test, 'isClient')) { - isInClient = true - } - } - } - - switch (env) { - case SERVER: - return isInServer && !isInClient - case CLIENT: - return !isInServer && isInClient - } -} - -export function isInServerBlock (ancestors) { - return isInBlock(ancestors, SERVER) -} - -export function isInClientBlock (ancestors) { - return isInBlock(ancestors, CLIENT) -} diff --git a/lib/util/executors/filterExecutorsByAncestors.js b/lib/util/executors/filterExecutorsByAncestors.js new file mode 100644 index 0000000000..43244d018a --- /dev/null +++ b/lib/util/executors/filterExecutorsByAncestors.js @@ -0,0 +1,33 @@ +import invariant from 'invariant' +import isMeteorBlockOnlyTest from './isMeteorBlockOnlyTest' +import getExecutorsFromTest from './getExecutorsFromTest' +import {intersection, difference} from './sets' + +// Set -> Array -> Set +export default function filterExecutorsByAncestors (originalExecutors, ancestors) { + + let executors = new Set([...originalExecutors]) + + for (let i = ancestors.length - 1; i > 0; i--) { + const current = ancestors[i] + const parent = ancestors[i - 1] + if (parent.type === 'IfStatement') { + if (isMeteorBlockOnlyTest(parent.test)) { + const executorsFromTest = getExecutorsFromTest(parent.test) + if (parent.consequent === current) { + executors = intersection(executors, executorsFromTest) + } else if (parent.alternate === current) { + executors = difference(executors, executorsFromTest) + } else { + invariant(false, 'Block is neither consequent nor alternate of parent') + } + } else { + + // can not determine executors, because of unresolvable if-statement + return new Set() + } + } + } + + return executors +} diff --git a/lib/util/executors/getExecutors.js b/lib/util/executors/getExecutors.js new file mode 100644 index 0000000000..fa4ba08f59 --- /dev/null +++ b/lib/util/executors/getExecutors.js @@ -0,0 +1,10 @@ +import filterExecutorsByAncestors from './filterExecutorsByAncestors' +import getExecutorsByEnv from './getExecutorsByEnv' + +// ENVIRONMENT -> Nodes -> Set +export default function getExecutors (env, ancestors) { + return filterExecutorsByAncestors( + getExecutorsByEnv(env), + ancestors + ) +} diff --git a/lib/util/executors/getExecutorsByEnv.js b/lib/util/executors/getExecutorsByEnv.js new file mode 100644 index 0000000000..e1acd60384 --- /dev/null +++ b/lib/util/executors/getExecutorsByEnv.js @@ -0,0 +1,51 @@ +import { + PUBLIC, + PRIVATE, + CLIENT, + SERVER, + PACKAGE, + TEST, + NODE_MODULE, + UNIVERSAL, + PACKAGE_CONFIG, + MOBILE_CONFIG, + COMPATIBILITY, + NON_METEOR +} from '../environment' + +/** + * Transforms an environment into executors + * @param {ENVIRONMENT} env An Environment + * @return {Set} A Set of executors + */ +export default function getExecutorsByEnv (env) { + const executors = new Set() + switch (env) { + case CLIENT: + case COMPATIBILITY: + executors.add('browser') + executors.add('cordova') + break + case SERVER: + executors.add('server') + break + case UNIVERSAL: + executors.add('server') + executors.add('browser') + executors.add('cordova') + break + case PACKAGE_CONFIG: + case MOBILE_CONFIG: + executors.add('isobuild') + break + case PUBLIC: + case PRIVATE: + case TEST: + case NODE_MODULE: + case NON_METEOR: + case PACKAGE: + default: + break + } + return executors +} diff --git a/lib/util/executors/getExecutorsFromTest.js b/lib/util/executors/getExecutorsFromTest.js new file mode 100644 index 0000000000..bed6b9428f --- /dev/null +++ b/lib/util/executors/getExecutorsFromTest.js @@ -0,0 +1,28 @@ +import invariant from 'invariant' +import isMeteorProp from '../ast/isMeteorProp' +import {union, intersection} from './sets' +import invert from './invert' + +// Nodes -> Set +export default function getExecutorsFromTest (test) { + switch (test.type) { + case 'MemberExpression': + if (isMeteorProp(test, 'isClient')) { + return new Set(['browser', 'cordova']) + } else if (isMeteorProp(test, 'isServer')) { + return new Set(['server']) + } else if (isMeteorProp(test, 'isCordova')) { + return new Set(['cordova']) + } + return invariant(false, 'Unkown Meteor prop should never be reached') + case 'UnaryExpression': + return invert(getExecutorsFromTest(test.argument)) + case 'LogicalExpression': + if (test.operator === '&&') { + return intersection(getExecutorsFromTest(test.left), getExecutorsFromTest(test.right)) + } else if (test.operator === '||') { + return union(getExecutorsFromTest(test.left), getExecutorsFromTest(test.right)) + } + return invariant(false, 'Unkown operator should never be reached') + } +} diff --git a/lib/util/executors/invert.js b/lib/util/executors/invert.js new file mode 100644 index 0000000000..2b49da3107 --- /dev/null +++ b/lib/util/executors/invert.js @@ -0,0 +1,5 @@ +import {difference} from './sets' + +export default function invert (executors) { + return difference(new Set(['browser', 'server', 'cordova']), executors) +} diff --git a/lib/util/executors/isMeteorBlockOnlyTest.js b/lib/util/executors/isMeteorBlockOnlyTest.js new file mode 100644 index 0000000000..0f5de3296d --- /dev/null +++ b/lib/util/executors/isMeteorBlockOnlyTest.js @@ -0,0 +1,28 @@ +import {isMeteorProp} from '../ast' + +/** + * Verifies a test of an IfStatement contains only checks with + * Meteor.isClient, Meteor.isServer and Meteor.isCordova. + * + * @param {node} test Test of an IfStatement (MemberExpression, LogicalExpression, UnaryExpression) + * @return {Boolean} True if test contains only Meteor locus checks + */ +export default function isMeteorBlockOnlyTest (test) { + switch (test.type) { + case 'MemberExpression': + return ( + isMeteorProp(test, 'isClient') || + isMeteorProp(test, 'isServer') || + isMeteorProp(test, 'isCordova') + ) + case 'UnaryExpression': + if (test.operator === '!') { + return isMeteorBlockOnlyTest(test.argument) + } + return false + case 'LogicalExpression': + return isMeteorBlockOnlyTest(test.left) && isMeteorBlockOnlyTest(test.right) + default: + return false + } +} diff --git a/lib/util/executors/sets.js b/lib/util/executors/sets.js new file mode 100644 index 0000000000..a1e357c6cc --- /dev/null +++ b/lib/util/executors/sets.js @@ -0,0 +1,26 @@ +import invariant from 'invariant' + +// Set -> Set -> Set +export function difference (a, b) { + invariant(!!a, 'difference: Set a is not defined') + invariant(!!b, 'difference: Set b is not defined') + return new Set( + [...a].filter(x => !b.has(x)) + ) +} + +// Set -> Set -> Set +export function union (a, b) { + invariant(!!a, 'union: Set a is not defined') + invariant(!!b, 'union: Set b is not defined') + return new Set([...a, ...b]) +} + +// Set -> Set -> Set +export function intersection (a, b) { + invariant(!!a, 'intersection: Set a is not defined') + invariant(!!b, 'intersection: Set b is not defined') + return new Set( + [...a].filter(element => b.has(element)) + ) +} diff --git a/lib/util/getMeta.js b/lib/util/getMeta.js deleted file mode 100644 index 862a41e1d3..0000000000 --- a/lib/util/getMeta.js +++ /dev/null @@ -1,6 +0,0 @@ -import getMeteorMeta from './internal/getMeteorMeta' -const getRelativePath = require('./internal/getRelativePath') - -export default function (filename) { - return getMeteorMeta(getRelativePath(filename)) -} diff --git a/lib/util/index.js b/lib/util/index.js new file mode 100644 index 0000000000..413f491f1c --- /dev/null +++ b/lib/util/index.js @@ -0,0 +1,2 @@ +export {default as getMeta} from './meta/getMeta' +export {default as getExecutors} from './executors/getExecutors' diff --git a/lib/util/internal/findOneUpwards.js b/lib/util/meta/findOneUpwards.js similarity index 100% rename from lib/util/internal/findOneUpwards.js rename to lib/util/meta/findOneUpwards.js diff --git a/lib/util/meta/getMeta.js b/lib/util/meta/getMeta.js new file mode 100644 index 0000000000..e56c5e726b --- /dev/null +++ b/lib/util/meta/getMeta.js @@ -0,0 +1,6 @@ +import getMeteorMeta from './getMeteorMeta' +const getRelativePath = require('./getRelativePath') + +export default function (filename) { + return getMeteorMeta(getRelativePath(filename)) +} diff --git a/lib/util/internal/getMeteorMeta.js b/lib/util/meta/getMeteorMeta.js similarity index 93% rename from lib/util/internal/getMeteorMeta.js rename to lib/util/meta/getMeteorMeta.js index 10771b08d6..175c94fccc 100644 --- a/lib/util/internal/getMeteorMeta.js +++ b/lib/util/meta/getMeteorMeta.js @@ -1,6 +1,7 @@ import path from 'path' -import ENVIRONMENT from '../environment.js' -import folderNames from '../folderNames.js' +import ENVIRONMENT from '../environment' +import folderNames from '../folderNames' +import isLintedEnv from './isLintedEnv' function matchLeft (dirs, list) { for (let i = 0; i < dirs.length; i++) { @@ -83,14 +84,14 @@ function determineEnvironment (pathInProjectList) { } - function getMeteorFileInfo (relativeFilename) { const pathInProjectList = relativeFilename.split(path.sep) const environment = determineEnvironment(pathInProjectList) return { path: relativeFilename, - env: environment + env: environment, + isLintedEnv: isLintedEnv(environment) } } diff --git a/lib/util/internal/getRelativePath.js b/lib/util/meta/getRelativePath.js similarity index 100% rename from lib/util/internal/getRelativePath.js rename to lib/util/meta/getRelativePath.js diff --git a/lib/util/internal/getRootPath.js b/lib/util/meta/getRootPath.js similarity index 100% rename from lib/util/internal/getRootPath.js rename to lib/util/meta/getRootPath.js diff --git a/lib/util/meta/isLintedEnv.js b/lib/util/meta/isLintedEnv.js new file mode 100644 index 0000000000..ca327438d3 --- /dev/null +++ b/lib/util/meta/isLintedEnv.js @@ -0,0 +1,5 @@ +import {CLIENT, SERVER, UNIVERSAL, COMPATIBILITY} from '../environment' + +export default function (env) { + return env === CLIENT || env === SERVER || env === UNIVERSAL || env === COMPATIBILITY +} diff --git a/lib/util/internal/isMeteorProject.js b/lib/util/meta/isMeteorProject.js similarity index 100% rename from lib/util/internal/isMeteorProject.js rename to lib/util/meta/isMeteorProject.js diff --git a/lib/util/internal/stripPathPrefix.js b/lib/util/meta/stripPathPrefix.js similarity index 100% rename from lib/util/internal/stripPathPrefix.js rename to lib/util/meta/stripPathPrefix.js diff --git a/package.json b/package.json index fb30e4c5b9..4373018356 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Meteor specific linting rules for ESLint", "main": "dist/index.js", "scripts": { - "build": "babel lib -d dist --auxiliary-comment-before \"istanbul ignore next\"", + "build": "babel lib -d dist --optional runtime --auxiliary-comment-before \"istanbul ignore next\"", "build:w": "npm run build -- -w", "check-coverage": "istanbul check-coverage", "clean": "rimraf dist", @@ -36,6 +36,7 @@ "devDependencies": { "babel": "5.8.23", "babel-eslint": "4.1.3", + "babel-runtime": "5.8.25", "colors": "1.1.2", "coveralls": "2.11.4", "cz-conventional-changelog": "1.1.4", diff --git a/scripts/new-rule.js b/scripts/new-rule.js index f2930a167b..c3ff98a2ad 100644 --- a/scripts/new-rule.js +++ b/scripts/new-rule.js @@ -66,11 +66,7 @@ import {CLIENT, SERVER, UNIVERSAL} from '../util/environment' module.exports = getMeta => context => { - const {env} = getMeta(context.getFilename()) - - if (env !== CLIENT && env !== SERVER && env !== UNIVERSAL) { - return {} - } + const {isLintedEnv} = getMeta(context.getFilename()) // --------------------------------------------------------------------------- // Helpers @@ -82,6 +78,10 @@ module.exports = getMeta => context => { // Public // --------------------------------------------------------------------------- + if (!isLintedEnv) { + return {} + } + return { // give me methods @@ -107,7 +107,7 @@ const test = `/** // Requirements // ----------------------------------------------------------------------------- -import {CLIENT, SERVER} from '../../../dist/util/environment.js' +import {CLIENT, SERVER} from '../../../dist/util/environment' const rule = require('../../../dist/rules/${ruleId}') const RuleTester = require('eslint').RuleTester @@ -117,7 +117,7 @@ const RuleTester = require('eslint').RuleTester // ----------------------------------------------------------------------------- const ruleTester = new RuleTester() -ruleTester.run('${ruleId}', rule(() => ({env: SERVER})), { +ruleTester.run('${ruleId}', rule(() => ({env: SERVER, isLintedEnv: true})), { valid: [ // fill me in @@ -127,14 +127,14 @@ ruleTester.run('${ruleId}', rule(() => ({env: SERVER})), { { code: '${escapedFailingExample}', errors: [ - {message: 'Unexpected Session statement.', type: 'MemberExpression'} + {message: 'The error message', type: 'MemberExpression'} ] } ] }) -ruleTester.run('${ruleId}', rule(() => ({env: CLIENT})), { +ruleTester.run('${ruleId}', rule(() => ({env: CLIENT, isLintedEnv: true})), { valid: [ // fill me in diff --git a/tests/lib/rules/audit-argument-checks.js b/tests/lib/rules/audit-argument-checks.js index 19d63532f3..b6fbbfa3e3 100644 --- a/tests/lib/rules/audit-argument-checks.js +++ b/tests/lib/rules/audit-argument-checks.js @@ -16,7 +16,7 @@ const RuleTester = require('eslint').RuleTester // ----------------------------------------------------------------------------- const ruleTester = new RuleTester() -ruleTester.run('audit-argument-checks', rule(), { +ruleTester.run('audit-argument-checks', rule(() => ({isLintedEnv: true})), { valid: [ 'foo()', @@ -123,3 +123,10 @@ ruleTester.run('audit-argument-checks', rule(), { } ] }) + +ruleTester.run('audit-argument-checks', rule(() => ({isLintedEnv: false})), { + valid: [ + 'Meteor.publish("foo", function (bar) { foo(); })' + ], + invalid: [] +}) diff --git a/tests/lib/rules/core.js b/tests/lib/rules/core.js index 4a4dc45daa..0c572625a7 100644 --- a/tests/lib/rules/core.js +++ b/tests/lib/rules/core.js @@ -9,15 +9,6 @@ // Requirements // ----------------------------------------------------------------------------- -import { - CLIENT, - SERVER, - UNIVERSAL, - MOBILE_CONFIG, - PACKAGE_CONFIG, - NON_METEOR -} from '../../../dist/util/environment.js' - const rule = require('../../../dist/rules/core') const RuleTester = require('eslint').RuleTester @@ -126,10 +117,5 @@ const errorFreeTests = { } const ruleTester = new RuleTester() -ruleTester.run('core', rule(() => ({env: SERVER})), tests) -ruleTester.run('core', rule(() => ({env: CLIENT})), tests) -ruleTester.run('core', rule(() => ({env: UNIVERSAL})), tests) - -ruleTester.run('core', rule(() => ({env: NON_METEOR})), errorFreeTests) -ruleTester.run('core', rule(() => ({env: PACKAGE_CONFIG})), errorFreeTests) -ruleTester.run('core', rule(() => ({env: MOBILE_CONFIG})), errorFreeTests) +ruleTester.run('core', rule(() => ({isLintedEnv: true})), tests) +ruleTester.run('core', rule(() => ({isLintedEnv: false})), errorFreeTests) diff --git a/tests/lib/rules/no-blaze-lifecycle-assignment.js b/tests/lib/rules/no-blaze-lifecycle-assignment.js index 82c75e8876..724900d405 100644 --- a/tests/lib/rules/no-blaze-lifecycle-assignment.js +++ b/tests/lib/rules/no-blaze-lifecycle-assignment.js @@ -18,7 +18,7 @@ const RuleTester = require('eslint').RuleTester // ----------------------------------------------------------------------------- const ruleTester = new RuleTester() -ruleTester.run('no-blaze-lifecycle-assignment', rule(), { +ruleTester.run('no-blaze-lifecycle-assignment', rule(() => ({isLintedEnv: true})), { valid: [ 'x += 1', @@ -74,3 +74,10 @@ ruleTester.run('no-blaze-lifecycle-assignment', rule(), { } ] }) + +ruleTester.run('no-blaze-lifecycle-assignment', rule(() => ({isLintedEnv: false})), { + valid: [ + 'Template.foo.created = function () {}' + ], + invalid: [] +}) diff --git a/tests/lib/rules/no-session.js b/tests/lib/rules/no-session.js index 5da7d4ceff..4176ef5ca6 100644 --- a/tests/lib/rules/no-session.js +++ b/tests/lib/rules/no-session.js @@ -9,37 +9,16 @@ // Requirements // ----------------------------------------------------------------------------- -import {SERVER, CLIENT} from '../../../dist/util/environment.js' const rule = require('../../../dist/rules/no-session') const RuleTester = require('eslint').RuleTester - -// ----------------------------------------------------------------------------- -// Environments -// ----------------------------------------------------------------------------- - -const serverEnv = { - path: 'server/methods.js', - env: SERVER, - isCompatibilityFile: false, - isInMeteorProject: true -} - -const clientEnv = { - path: 'client/methods.js', - env: CLIENT, - isCompatibilityFile: false, - isInMeteorProject: true -} - - // ----------------------------------------------------------------------------- // Tests // ----------------------------------------------------------------------------- const ruleTester = new RuleTester() -ruleTester.run('no-session', rule(() => serverEnv), { +ruleTester.run('no-session', rule(() => ({isLintedEnv: true})), { valid: [ 'session.get("foo")', @@ -47,26 +26,17 @@ ruleTester.run('no-session', rule(() => serverEnv), { ], invalid: [ - {code: 'Session.set("foo", true)', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.get("foo")', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.clear("foo")', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.all()', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]} + {code: 'Session.set("foo", true)', errors: [{message: 'Unexpected Session statement', type: 'MemberExpression'}]}, + {code: 'Session.get("foo")', errors: [{message: 'Unexpected Session statement', type: 'MemberExpression'}]}, + {code: 'Session.clear("foo")', errors: [{message: 'Unexpected Session statement', type: 'MemberExpression'}]}, + {code: 'Session.all()', errors: [{message: 'Unexpected Session statement', type: 'MemberExpression'}]} ] }) - -ruleTester.run('no-session', rule(() => clientEnv), { - +ruleTester.run('no-session', rule(() => ({isLintedEnv: false})), { valid: [ - 'session.get("foo")', - 'foo(Session)' + 'Session.set("foo", true)', + 'Session.get("foo")' ], - - invalid: [ - {code: 'Session.set("foo", true)', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.get("foo")', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.clear("foo")', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]}, - {code: 'Session.all()', errors: [{message: 'Unexpected Session statement.', type: 'MemberExpression'}]} - ] - + invalid: [] }) diff --git a/tests/lib/rules/no-zero-timeout.js b/tests/lib/rules/no-zero-timeout.js index 7b47c4061b..65bc7f281c 100644 --- a/tests/lib/rules/no-zero-timeout.js +++ b/tests/lib/rules/no-zero-timeout.js @@ -16,7 +16,7 @@ const RuleTester = require('eslint').RuleTester // ----------------------------------------------------------------------------- const ruleTester = new RuleTester() -ruleTester.run('no-zero-timeout', rule(), { +ruleTester.run('no-zero-timeout', rule(() => ({isLintedEnv: true})), { valid: [ 'Meteor.setTimeout()', @@ -66,3 +66,10 @@ ruleTester.run('no-zero-timeout', rule(), { } ] }) + +ruleTester.run('no-zero-timeout', rule(() => ({isLintedEnv: false})), { + valid: [ + 'Meteor.setTimeout(function () {}, 0)' + ], + invalid: [] +}) diff --git a/tests/lib/rules/pubsub.js b/tests/lib/rules/pubsub.js index 15c31e873d..32ca6dae33 100644 --- a/tests/lib/rules/pubsub.js +++ b/tests/lib/rules/pubsub.js @@ -13,49 +13,12 @@ import {CLIENT, SERVER, UNIVERSAL} from '../../../dist/util/environment.js' const rule = require('../../../dist/rules/pubsub') const RuleTester = require('eslint').RuleTester - -// ----------------------------------------------------------------------------- -// Environments -// ----------------------------------------------------------------------------- - -const serverEnv = { - path: 'server/pubsub.js', - env: SERVER, - isCompatibilityFile: false, - isInMeteorProject: true, - isPackageConfig: false, - isMobileConfig: false -} - -const clientEnv = { - path: 'server/pubsub.js', - env: CLIENT, - isCompatibilityFile: false, - isInMeteorProject: true, - isPackageConfig: false, - isMobileConfig: false -} - -const universalEnv = { - path: 'pubsub.js', - env: UNIVERSAL, - isCompatibilityFile: false, - isInMeteorProject: true, - isPackageConfig: false, - isMobileConfig: false -} - -const notInMeteorProject = { - isInMeteorProject: false -} - // ----------------------------------------------------------------------------- // Tests // ----------------------------------------------------------------------------- - const ruleTester = new RuleTester() -ruleTester.run('pubsub', rule(() => serverEnv), { +ruleTester.run('pubsub', rule(() => ({env: SERVER, isLintedEnv: true})), { valid: [ 'Meteor.publish("foo", function () {})' ], @@ -76,7 +39,7 @@ ruleTester.run('pubsub', rule(() => serverEnv), { ] }) -ruleTester.run('pubsub', rule(() => clientEnv), { +ruleTester.run('pubsub', rule(() => ({env: CLIENT, isLintedEnv: true})), { valid: [ 'Meteor.subscribe("foo")' ], @@ -97,10 +60,20 @@ ruleTester.run('pubsub', rule(() => clientEnv), { ] }) -ruleTester.run('pubsub', rule(() => universalEnv), { +ruleTester.run('pubsub', rule(() => ({env: UNIVERSAL, isLintedEnv: true})), { valid: [ 'if (Meteor.isClient) { Meteor.subscribe("foo") }', - 'if (Meteor.isServer) { Meteor.publish("foo", function () {}) }' + 'if (Meteor.isServer) { Meteor.publish("foo", function () {}) }', + ` + if (Meteor.isClient) { + if (Meteor.isServer) { + + // valid because it is unreachable + Meteor.publish("foo", function () {}) + Meteor.subscribe("foo") + } + } + ` ], invalid: [ @@ -109,39 +82,11 @@ ruleTester.run('pubsub', rule(() => universalEnv), { errors: [ {message: 'Allowed on client only', type: 'CallExpression'} ] - }, - { - code: ` - if (Meteor.isClient) { - if (Meteor.isServer) { - Meteor.publish("foo", function () {}) - Meteor.subscribe("foo") - } - } - `, - errors: [ - {message: 'Allowed on server only', type: 'CallExpression'}, - {message: 'Allowed on client only', type: 'CallExpression'} - ] - }, - { - code: ` - if (Meteor.isServer) { - if (Meteor.isClient) { - Meteor.publish("foo", function () {}) - Meteor.subscribe("foo") - } - } - `, - errors: [ - {message: 'Allowed on server only', type: 'CallExpression'}, - {message: 'Allowed on client only', type: 'CallExpression'} - ] } ] }) -ruleTester.run('pubsub', rule(() => notInMeteorProject), { +ruleTester.run('pubsub', rule(() => ({isLintedEnv: false})), { valid: [ 'foo()' ], diff --git a/tests/lib/util/ast/isInBlock.js b/tests/lib/util/ast/isInBlock.js deleted file mode 100644 index b4605ed4c2..0000000000 --- a/tests/lib/util/ast/isInBlock.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-env mocha */ - -import assert from 'assert' -import {CLIENT, SERVER, UNIVERSAL} from '../../../../dist/util/environment' -import {isInBlock, isInServerBlock, isInClientBlock} from '../../../../dist/util/ast/isInBlock' - -describe('isInBlock', function () { - it('returns false if no ancestors are present', function () { - assert.equal(isInBlock([], CLIENT), false) - assert.equal(isInBlock([], SERVER), false) - }) - - it('throws if no ancestors are given', function () { - assert.throws(isInClientBlock, Error) - assert.throws(isInServerBlock, Error) - }) - - it('throws if no environment is given', function () { - assert.throws(isInBlock.bind(null, []), Error) - }) - - it('throws when called with unhandeled environment', function () { - assert.throws(isInBlock.bind(null, [], UNIVERSAL)) - }) -}) diff --git a/tests/lib/util/executors/filterExecutorsByAncestors.js b/tests/lib/util/executors/filterExecutorsByAncestors.js new file mode 100644 index 0000000000..c6663b3c1f --- /dev/null +++ b/tests/lib/util/executors/filterExecutorsByAncestors.js @@ -0,0 +1,190 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import filterExecutorsByAncestors from '../../../../dist/util/executors/filterExecutorsByAncestors' + +describe('filterExecutorsByAncestors', function () { + + it('filters on MemberExpression for isClient', function () { + const consequent = {type: 'BlockStatement'} + const result = filterExecutorsByAncestors(new Set(['browser', 'server']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }, + consequent: consequent + }, + consequent + ]) + assert.equal(result.size, 1) + assert.ok(result.has('browser')) + }) + + it('filters on MemberExpression for else-block of isClient', function () { + const alternate = {type: 'BlockStatement'} + const result = filterExecutorsByAncestors(new Set(['browser', 'server']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }, + alternate: alternate + }, + alternate + ]) + assert.equal(result.size, 1) + assert.ok(result.has('server')) + }) + + it('warns on hierarchical error', function () { + assert.throws(() => { + const consequent = {type: 'BlockStatement'} + filterExecutorsByAncestors(new Set(['browser', 'server']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }, + consequent + ]) + }) + }) + + it('filters on MemberExpression for isServer', function () { + const consequent = {type: 'BlockStatement'} + const result = filterExecutorsByAncestors(new Set(['server', 'cordova']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + }, + consequent: consequent + }, + consequent + ]) + assert.equal(result.size, 1) + assert.ok(result.has('server')) + }) + + it('filters on MemberExpression for isCordova', function () { + const consequent = {type: 'BlockStatement'} + const result = filterExecutorsByAncestors(new Set(['browser', 'cordova']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isCordova' + } + }, + consequent: consequent + }, + consequent + ]) + assert.equal(result.size, 1) + assert.ok(result.has('cordova')) + }) + + it('filters on UnaryExpression', function () { + const consequent = {type: 'BlockStatement'} + const result = filterExecutorsByAncestors(new Set(['browser', 'server', 'cordova']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: { + type: 'UnaryExpression', + operator: '!', + argument: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }, + consequent: consequent + }, + consequent + ]) + assert.equal(result.size, 1) + assert.ok(result.has('server')) + }) + + it('returns no executors when an unresolvable IfStatement is in ancestors', function () { + const consequent = {type: 'BlockStatement'} + const ifConsequent = { + test: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }, + consequent: consequent + } + const result = filterExecutorsByAncestors(new Set(['browser', 'server']), [ + {type: 'Program'}, + { + type: 'IfStatement', + test: {type: 'Identifier'}, + consequent: ifConsequent + }, + ifConsequent, + consequent + ]) + assert.equal(result.size, 0) + }) + +}) diff --git a/tests/lib/util/executors/getExecutors.js b/tests/lib/util/executors/getExecutors.js new file mode 100644 index 0000000000..2af63e96aa --- /dev/null +++ b/tests/lib/util/executors/getExecutors.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import getExecutors from '../../../../dist/util/executors/getExecutors' +import {UNIVERSAL} from '../../../../dist/util/environment' + +describe('getExecutors', function () { + it('returns executors for no ancestors', function () { + const result = getExecutors(UNIVERSAL, []) + assert.equal(result.size, 3) + assert.ok(result.has('browser')) + assert.ok(result.has('server')) + assert.ok(result.has('cordova')) + }) +}) diff --git a/tests/lib/util/executors/getExecutorsByEnv.js b/tests/lib/util/executors/getExecutorsByEnv.js new file mode 100644 index 0000000000..0c6f2832de --- /dev/null +++ b/tests/lib/util/executors/getExecutorsByEnv.js @@ -0,0 +1,80 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import getExecutorsByEnv from '../../../../dist/util/executors/getExecutorsByEnv' + +import { + PUBLIC, + PRIVATE, + CLIENT, + SERVER, + PACKAGE, + TEST, + NODE_MODULE, + UNIVERSAL, + PACKAGE_CONFIG, + MOBILE_CONFIG, + COMPATIBILITY, + NON_METEOR +} from '../../../../dist/util/environment' + +describe('getExecutorsByEnv', function () { + it('public', function () { + const result = getExecutorsByEnv(PUBLIC) + assert.equal(result.size, 0) + }) + it('private', function () { + const result = getExecutorsByEnv(PRIVATE) + assert.equal(result.size, 0) + }) + it('client', function () { + const result = getExecutorsByEnv(CLIENT) + assert.equal(result.size, 2) + assert.ok(result.has('browser')) + assert.ok(result.has('cordova')) + }) + it('server', function () { + const result = getExecutorsByEnv(SERVER) + assert.equal(result.size, 1) + assert.ok(result.has('server')) + }) + it('package', function () { + const result = getExecutorsByEnv(PACKAGE) + assert.equal(result.size, 0) + }) + it('test', function () { + const result = getExecutorsByEnv(TEST) + assert.equal(result.size, 0) + }) + it('node_module', function () { + const result = getExecutorsByEnv(NODE_MODULE) + assert.equal(result.size, 0) + }) + it('universal', function () { + const result = getExecutorsByEnv(UNIVERSAL) + assert.equal(result.size, 3) + assert.ok(result.has('browser')) + assert.ok(result.has('server')) + assert.ok(result.has('cordova')) + }) + it('packageConfig', function () { + const result = getExecutorsByEnv(PACKAGE_CONFIG) + assert.equal(result.size, 1) + assert.ok(result.has('isobuild')) + }) + it('mobileConfig', function () { + const result = getExecutorsByEnv(MOBILE_CONFIG) + assert.equal(result.size, 1) + assert.ok(result.has('isobuild')) + }) + it('compatibility', function () { + const result = getExecutorsByEnv(COMPATIBILITY) + assert.equal(result.size, 2) + assert.ok(result.has('cordova')) + assert.ok(result.has('browser')) + }) + it('nonMeteor', function () { + const result = getExecutorsByEnv(NON_METEOR) + assert.equal(result.size, 0) + }) +}) diff --git a/tests/lib/util/executors/getExecutorsFromTest.js b/tests/lib/util/executors/getExecutorsFromTest.js new file mode 100644 index 0000000000..d1046e4fb1 --- /dev/null +++ b/tests/lib/util/executors/getExecutorsFromTest.js @@ -0,0 +1,149 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import getExecutorsFromTest from '../../../../dist/util/executors/getExecutorsFromTest' + +describe('getExecutorsFromTest', function () { + describe('MemberExpression', function () { + it('isClient', function () { + const result = getExecutorsFromTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }) + assert.equal(result.size, 2) + assert.ok(result.has('browser')) + assert.ok(result.has('cordova')) + }) + it('isServer', function () { + const result = getExecutorsFromTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + }) + assert.equal(result.size, 1) + assert.ok(result.has('server')) + }) + it('isCordova', function () { + const result = getExecutorsFromTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isCordova' + } + }) + assert.equal(result.size, 1) + assert.ok(result.has('cordova')) + }) + it('throws on unkown Meteor prop', function () { + assert.throws( + () => { + getExecutorsFromTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isNotAMeteorProp' + } + }) + } + ) + }) + }) + + + describe('LogicalExpression', function () { + it('resolves isServer AND isClient', function () { + const result = getExecutorsFromTest({ + type: 'LogicalExpression', + operator: '&&', + left: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }) + assert.equal(result.size, 0) + }) + + it('resolves isServer OR isClient', function () { + const result = getExecutorsFromTest({ + type: 'LogicalExpression', + operator: '||', + left: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }) + assert.equal(result.size, 3) + assert.ok(result.has('browser')) + assert.ok(result.has('server')) + assert.ok(result.has('cordova')) + }) + + it('throws for unkown operator in LogicalExpression', function () { + assert.throws(() => { + getExecutorsFromTest({ + type: 'LogicalExpression', + operator: 'XY', + left: {}, + right: {} + }) + }) + }) + }) +}) diff --git a/tests/lib/util/executors/isMeteorBlockOnlyTest.js b/tests/lib/util/executors/isMeteorBlockOnlyTest.js new file mode 100644 index 0000000000..3f94505488 --- /dev/null +++ b/tests/lib/util/executors/isMeteorBlockOnlyTest.js @@ -0,0 +1,206 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import isMeteorBlockOnlyTest from '../../../../dist/util/executors/isMeteorBlockOnlyTest' + +describe('isMeteorBlockOnlyTest', function () { + + it('accepts a valid MemberExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }) + assert.ok(result) + }) + + it('accepts a valid computed MemberExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'MemberExpression', + computed: true, + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Literal', + value: 'isCordova' + } + }) + assert.ok(result) + }) + + it('does not accept an invalid MemberExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Foo' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }) + assert.ok(!result) + }) + + it('accepts a valid UnaryExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'UnaryExpression', + operator: '!', + argument: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + } + }) + assert.ok(result) + }) + + it('does not accept an invalid UnaryExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'UnaryExpression', + operator: '!', + argument: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Foo' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }) + assert.ok(!result) + }) + + it('accepts a valid LogicalExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'LogicalExpression', + operator: '||', + left: { + type: 'LogicalExpression', + operator: '&&', + left: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isCordova' + } + } + }) + assert.ok(result) + }) + + it('does not accept an invalid LogicalExpression', function () { + const result = isMeteorBlockOnlyTest({ + type: 'LogicalExpression', + operator: '||', + left: { + type: 'LogicalExpression', + operator: '&&', + left: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Foo' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isServer' + } + } + }, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Meteor' + }, + property: { + type: 'Identifier', + name: 'isCordova' + } + } + }) + assert.ok(!result) + }) + + it('returns false for unresolvable expressions', function () { + const result = isMeteorBlockOnlyTest({type: 'Identifier'}) + assert.ok(!result) + }) + + it('returns false for invalid unary expressions', function () { + const result = isMeteorBlockOnlyTest({ + type: 'UnaryExpression', + operator: '-', + argument: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Foo' + }, + property: { + type: 'Identifier', + name: 'isClient' + } + } + }) + assert.ok(!result) + }) + +}) diff --git a/tests/lib/util/executors/sets.js b/tests/lib/util/executors/sets.js new file mode 100644 index 0000000000..ed99df955f --- /dev/null +++ b/tests/lib/util/executors/sets.js @@ -0,0 +1,39 @@ +/* eslint-env mocha */ + +import assert from 'assert' +import {difference, union, intersection} from '../../../../dist/util/executors/sets' + +describe('executors', function () { + + describe('union', function () { + it('unifies two sets', function () { + const result = union(new Set(['cordova']), new Set(['client', 'server'])) + assert.equal(result.size, 3) + assert.ok(result.has('client')) + assert.ok(result.has('cordova')) + assert.ok(result.has('server')) + }) + }) + + describe('difference', function () { + it('returns the difference when b contains nothing from a', function () { + const result = difference(new Set(['cordova']), new Set(['client', 'server'])) + assert.equal(result.size, 1) + assert.ok(result.has('cordova')) + }) + + it('returns the difference when b contains one value from a', function () { + const result = difference(new Set(['client', 'cordova']), new Set(['client', 'server'])) + assert.equal(result.size, 1) + assert.ok(result.has('cordova')) + }) + }) + + describe('intersection', function () { + it('returns the intersection', function () { + const result = intersection(new Set(['client', 'cordova']), new Set(['client', 'server'])) + assert.equal(result.size, 1) + assert.ok(result.has('client')) + }) + }) +}) diff --git a/tests/lib/util/internal/findOneUpwards.js b/tests/lib/util/meta/findOneUpwards.js similarity index 92% rename from tests/lib/util/internal/findOneUpwards.js rename to tests/lib/util/meta/findOneUpwards.js index 8f6a68c874..af78e42e8a 100644 --- a/tests/lib/util/internal/findOneUpwards.js +++ b/tests/lib/util/meta/findOneUpwards.js @@ -13,7 +13,7 @@ function matcher (filename) { ) } -const findOneUpwards = require('../../../../dist/util/internal/findOneUpwards.js') +const findOneUpwards = require('../../../../dist/util/meta/findOneUpwards.js') describe('findOneUpwards', function () { it('returns false when no project is found', function () { diff --git a/tests/lib/util/getMeta.js b/tests/lib/util/meta/getMeta.js similarity index 81% rename from tests/lib/util/getMeta.js rename to tests/lib/util/meta/getMeta.js index eff6b66a3c..d7966a0eef 100644 --- a/tests/lib/util/getMeta.js +++ b/tests/lib/util/meta/getMeta.js @@ -1,10 +1,10 @@ /* eslint-env mocha */ import assert from 'assert' -import {NON_METEOR} from '../../../dist/util/environment' +import {NON_METEOR} from '../../../../dist/util/environment' const rewire = require('rewire') -const getMeta = rewire('../../../dist/util/getMeta') +const getMeta = rewire('../../../../dist/util/meta/getMeta') getMeta.__set__('getRelativePath', function (path) { return path }) diff --git a/tests/lib/util/internal/getMeteorMeta.js b/tests/lib/util/meta/getMeteorMeta.js similarity index 98% rename from tests/lib/util/internal/getMeteorMeta.js rename to tests/lib/util/meta/getMeteorMeta.js index dac30fe2bb..695cf986fa 100644 --- a/tests/lib/util/internal/getMeteorMeta.js +++ b/tests/lib/util/meta/getMeteorMeta.js @@ -4,7 +4,7 @@ import assert from 'assert' import path from 'path' import ENVIRONMENT from '../../../../dist/util/environment.js' -import getMeteorMeta from '../../../../dist/util/internal/getMeteorMeta.js' +import getMeteorMeta from '../../../../dist/util/meta/getMeteorMeta.js' describe('getMeteorMeta', function () { diff --git a/tests/lib/util/internal/getRelativePath.js b/tests/lib/util/meta/getRelativePath.js similarity index 88% rename from tests/lib/util/internal/getRelativePath.js rename to tests/lib/util/meta/getRelativePath.js index 1c70fad1b8..6c61fd6509 100644 --- a/tests/lib/util/internal/getRelativePath.js +++ b/tests/lib/util/meta/getRelativePath.js @@ -2,7 +2,7 @@ import assert from 'assert' const rewire = require('rewire') -const getRelativePath = rewire('../../../../dist/util/internal/getRelativePath.js') +const getRelativePath = rewire('../../../../dist/util/meta/getRelativePath.js') getRelativePath.__set__('getRootPath', function (filename) { if (filename === '/Users/anon/git/meteor-project/client/file.js') { diff --git a/tests/lib/util/internal/getRootPath.js b/tests/lib/util/meta/getRootPath.js similarity index 91% rename from tests/lib/util/internal/getRootPath.js rename to tests/lib/util/meta/getRootPath.js index 03765100c9..77344c9a5e 100644 --- a/tests/lib/util/internal/getRootPath.js +++ b/tests/lib/util/meta/getRootPath.js @@ -2,7 +2,7 @@ import assert from 'assert' const rewire = require('rewire') -const getRootPath = rewire('../../../../dist/util/internal/getRootPath.js') +const getRootPath = rewire('../../../../dist/util/meta/getRootPath.js') getRootPath.__set__('findOneUpwards', function (filename) { if (filename === '/Users/anon/git/meteor-project/file.js') { diff --git a/tests/lib/util/internal/isMeteorProject.js b/tests/lib/util/meta/isMeteorProject.js similarity index 87% rename from tests/lib/util/internal/isMeteorProject.js rename to tests/lib/util/meta/isMeteorProject.js index 7842b93458..c509c497f8 100644 --- a/tests/lib/util/internal/isMeteorProject.js +++ b/tests/lib/util/meta/isMeteorProject.js @@ -2,7 +2,7 @@ import assert from 'assert' const rewire = require('rewire') -const isMeteorProject = rewire('../../../../dist/util/internal/isMeteorProject') +const isMeteorProject = rewire('../../../../dist/util/meta/isMeteorProject') isMeteorProject.__set__('pathExists', { sync: function (path) { diff --git a/tests/lib/util/internal/stripPathPrefix.js b/tests/lib/util/meta/stripPathPrefix.js similarity index 81% rename from tests/lib/util/internal/stripPathPrefix.js rename to tests/lib/util/meta/stripPathPrefix.js index 3c9f30c0b4..b825065bef 100644 --- a/tests/lib/util/internal/stripPathPrefix.js +++ b/tests/lib/util/meta/stripPathPrefix.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ import assert from 'assert' -import stripPathPrefix from '../../../../dist/util/internal/stripPathPrefix.js' +import stripPathPrefix from '../../../../dist/util/meta/stripPathPrefix.js' describe('stripPathPrefix', function () { it('strips path correctly', function () {