From 3d629dbc0a2239533393d0318039e2ca9e59fca5 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Wed, 17 Jul 2019 10:15:21 +0930 Subject: [PATCH 01/43] chore(all): add package-lock.json to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 782c2cb..4b56bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ dist/ node_modules/ npm-debug.log coverage/ +package-lock.json \ No newline at end of file From 10d9a25648e34bbde47f9b2996d533adab99dd34 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Wed, 17 Jul 2019 11:14:58 +0930 Subject: [PATCH 02/43] chore(prettier): add prettier --- .prettierignore | 34 ++++++++++++++++++++++++++++++++++ .prettierrc.js | 19 +++++++++++++++++++ package.json | 3 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..792bb9d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,34 @@ +*.min.js +**/node_modules/** +flow-typed + +# webfont demo styles +**/specimen_files + +# built sites +benchmarks/**/public +e2e-tests/**/public +examples/**/public +integration-tests/**/public +www/public + +# cache-dirs +**/.cache + +# ignore built packages +packages/**/*.js +!packages/gatsby/cache-dir/**/*.js +!packages/*/src/**/*.js +packages/gatsby/cache-dir/commonjs/**/*.js + +# fixtures +**/__testfixtures__/** +**/__tests__/fixtures/** + +infrastructure + +# coverage +coverage + +# forestry files +.forestry/**/* diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..da515b4 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,19 @@ +module.exports = { + endOfLine: 'lf', + semi: true, + singleQuote: true, + useTabs: true, + trailingComma: 'es5', + overrides: [ + { + // This file uses semicolons. It's needed here because `documentation` + // package (used to parse jsdoc and provide content for API reference pages) + // behaviour is inconsistent when not using semicolons after + // object declarations. + files: ['**/api-node-helpers-docs.js'], + options: { + semi: true, + }, + }, + ], +} diff --git a/package.json b/package.json index 6872e05..26db049 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "major": "release major" }, "dependencies": { - "eslint-plugin-sort-class-members": "^1.0.1" + "eslint-plugin-sort-class-members": "^1.0.1", + "prettier": "^1.18.2" }, "peerDependencies": { "eslint": ">=0.8.0" From 4c8844418dea84d1442f3a03e614ec47c5799e3f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Wed, 17 Jul 2019 13:15:34 +0930 Subject: [PATCH 03/43] chore(babel): extract config out of package.json into .babelrc Allows running mocha on the command line --- .babelrc | 3 +++ package.json | 10 +++------- 2 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 .babelrc diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..297061b --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + presets: ['stage-1', 'es2015'] +} diff --git a/package.json b/package.json index 6872e05..45e902a 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,11 @@ "devDependencies": { "@bryanrsmith/eslint-config-standard": "^2.1.3", "babel-cli": "^6.9.0", + "babel-core": "^6.26.3", "babel-eslint": "^6.0.4", "babel-preset-es2015": "^6.9.0", "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.26.0", "coveralls": "^2.11.9", "eslint": "^2.13.0", "isparta": "^4.0.0", @@ -52,11 +54,5 @@ "engines": { "node": ">=4.0.0" }, - "license": "MIT", - "babel": { - "presets": [ - "stage-1", - "es2015" - ] - } + "license": "MIT" } From 25793a579efc7883b40748211188f7638d2e90d5 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Wed, 17 Jul 2019 15:21:27 +0930 Subject: [PATCH 04/43] chore(mocha): use test/mocha.opts file to specify babel loading Avoids the need to run babel-node directly. Allows calling `mocha test/rules/` directly. --- package.json | 2 +- test/mocha.opts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/mocha.opts diff --git a/package.json b/package.json index 6872e05..0cbc5ef 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "scripts": { "build": "npm run lint && npm run test && rm -rf dist && babel src --out-dir dist", - "test": "babel-node node_modules/.bin/isparta cover --report text-summary --report lcov node_modules/mocha/bin/_mocha -- --recursive", + "test": "node node_modules/isparta/bin/isparta cover --report text-summary --report lcov node_modules/mocha/bin/_mocha -- --recursive", "lint": "eslint src/ test/", "ci": "npm run build && cat coverage/lcov.info | node_modules/.bin/coveralls", "patch": "release patch", diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..52fdec4 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ + --compilers js:babel-register \ No newline at end of file From b17582de152e1e4c2d9457e65a60912c1d5602e2 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Wed, 17 Jul 2019 17:39:32 +0930 Subject: [PATCH 05/43] feat(rule): add rule for webpack-entry-point --- src/rules/webpack-entry-point.js | 223 +++++++++++++++++++++++++ test/rules/webpack-entry-point.spec.js | 40 +++++ 2 files changed, 263 insertions(+) create mode 100644 src/rules/webpack-entry-point.js create mode 100644 test/rules/webpack-entry-point.spec.js diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js new file mode 100644 index 0000000..7a83f6f --- /dev/null +++ b/src/rules/webpack-entry-point.js @@ -0,0 +1,223 @@ +const path = require('path'); + +/* + Ensure that the `webpack.config.js` file correctly specifies the entry point + as `aurelia-bootstrapper`. + + It expects the export to look something like this (exporting either a function or object) + + module.exports = function () { + return { + entry: { + app: ['aurelia-bootstrapper'] + }, + } + }; +*/ + +const webpackConfigFileName = 'webpack.config.js'; + +const types = { + ArrayExpression: 'ArrayExpression', + AssignmentExpression: 'AssignmentExpression', + BlockStatement: 'BlockStatement', + ExpressionStatement: 'ExpressionStatement', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + MemberExpression: 'MemberExpression', + ObjectExpression: 'ObjectExpression', + Property: 'Property', + ReturnStatement: 'ReturnStatement', +}; + +const isAssignedToModuleExports = context => { + const ancestors = context.getAncestors(); + + const appProperty = ancestors.pop(); + if ( + !appProperty || + appProperty.type !== types.Property || + appProperty.key.name !== 'app' + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring appProperty', appProperty); + + return false; + } + + const entryObjectExpression = ancestors.pop(); + if ( + !entryObjectExpression || + entryObjectExpression.type !== types.ObjectExpression + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring entryObjectExpression', entryObjectExpression); + return false; + } + + const entryProperty = ancestors.pop(); + if ( + !entryProperty || + entryProperty.type !== types.Property || + entryProperty.key.name !== 'entry' + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring entryProperty', entryProperty); + return false; + } + + const objectExpression = ancestors.pop(); + if (!objectExpression || objectExpression.type !== types.ObjectExpression) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring objectExpression', objectExpression); + return false; + } + + // TODO Handle direct assignment rather than functions + // TODO Handle arrow functions with body + // TODO Handle arrow functions with ObjectExpressions + const returnStatement = ancestors.pop(); + if (!returnStatement || returnStatement.type !== types.ReturnStatement) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring returnStatement', returnStatement); + return false; + } + + const blockStatement = ancestors.pop(); + if (!blockStatement || blockStatement.type !== types.BlockStatement) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring blockStatement', blockStatement); + return false; + } + + const functionExpression = ancestors.pop(); + if ( + !functionExpression || + functionExpression.type !== types.FunctionExpression + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring functionExpression', functionExpression); + return false; + } + + const assignmentExpression = ancestors.pop(); + if ( + !assignmentExpression || + assignmentExpression.type !== types.AssignmentExpression || + assignmentExpression.operator !== '=' + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring assignmentExpression', assignmentExpression); + return false; + } + + const expressionStatement = ancestors.pop(); + if ( + !expressionStatement || + expressionStatement.type !== types.ExpressionStatement + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring expressionStatement', expressionStatement); + return false; + } + + const leftHandSideOfExpressionStatement = expressionStatement.expression.left; + if ( + !leftHandSideOfExpressionStatement || + leftHandSideOfExpressionStatement.type !== types.MemberExpression + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log( + 'Ignoring leftHandSideOfExpressionStatement', + leftHandSideOfExpressionStatement + ); + return false; + } + + const object = leftHandSideOfExpressionStatement.object; + if (!object || object.type !== types.Identifier || object.name !== 'module') { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring object', object); + return false; + } + + const property = leftHandSideOfExpressionStatement.property; + if ( + !property || + property.type !== types.Identifier || + property.name !== 'exports' + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring property', property); + return false; + } + + return true; +}; + +const webpackEntryPointIsAureliaBootrap = context => node => { + const basename = path.basename(context.getFilename()); + + // Only webpack.config.js is checked + if (basename !== webpackConfigFileName) { + return; + } + + if (node.name === 'app') { + if (!isAssignedToModuleExports(context)) { + return; + } + + const parent = node.parent; + const value = parent.value; + if (value.type !== types.ArrayExpression) { + context.report({ + node, + message: 'entry.app must be an array of strings', + }); + return; + } + const elements = value.elements; + if (elements.length !== 1 || elements[0] !== 'aurelia-bootstrapper') { + context.report({ + node, + message: + "entry.app must be ['aurelia-bootstrapper']: found {{ value }}", + data: { + value: context.getSourceCode().getText(value), + }, + }); + return; + } + } +}; + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'TODO', + category: 'aurelia', + fixable: 'code', + }, + }, + create: context => { + // state here + + return { + Identifier: webpackEntryPointIsAureliaBootrap(context), + }; + }, +}; diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js new file mode 100644 index 0000000..276b218 --- /dev/null +++ b/test/rules/webpack-entry-point.spec.js @@ -0,0 +1,40 @@ +import eslint from 'eslint'; +import rule from '../../src/rules/webpack-entry-point'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +const moduleExportsEntryAppNotAurelia = ` + module.exports = function () { + return { + entry: { + app: ['not-aurelia-bootstrapper'] + }, + } + }; +`; + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('webpack-entry-point', rule, { + valid: [ + { + // Only webpack.config.js is checked + filename: '/some/dir/not.webpack.config.js', + code: `// filename=/some/dir/not.webpack.config.js ${moduleExportsEntryAppNotAurelia}`, + }, + ], + invalid: [ + { + filename: '/some/dir/webpack.config.js', + code: `// filename=/some/dir/webpack.config.js ${moduleExportsEntryAppNotAurelia}`, + moduleExportsEntryAppNotAurelia, + errors: [ + { + message: + "entry.app must be ['aurelia-bootstrapper']: found ['not-aurelia-bootstrapper']", + type: 'Identifier', + }, + ], + }, + ], +}); From 160583dda47cb6cf1382d8b18e689c74723c4994 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 10:04:23 +0930 Subject: [PATCH 06/43] refactor(rule/webpack-entry-point): use embedFilenameInCode --- test/rules/webpack-entry-point.spec.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js index 276b218..8b881ad 100644 --- a/test/rules/webpack-entry-point.spec.js +++ b/test/rules/webpack-entry-point.spec.js @@ -13,21 +13,29 @@ const moduleExportsEntryAppNotAurelia = ` }; `; +const embedFilenameInCode = ({ filename, code }) => ({ + filename, + code: `// filename=${filename}\n${code}`, +}); + // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. ruleTester.run('webpack-entry-point', rule, { valid: [ { - // Only webpack.config.js is checked - filename: '/some/dir/not.webpack.config.js', - code: `// filename=/some/dir/not.webpack.config.js ${moduleExportsEntryAppNotAurelia}`, + // Only webpack.config.js is checked for valid entry.app values + ...embedFilenameInCode({ + filename: '/some/dir/not.webpack.config.js', + code: moduleExportsEntryAppNotAurelia, + }), }, ], invalid: [ { - filename: '/some/dir/webpack.config.js', - code: `// filename=/some/dir/webpack.config.js ${moduleExportsEntryAppNotAurelia}`, - moduleExportsEntryAppNotAurelia, + ...embedFilenameInCode({ + filename: '/some/dir/webpack.config.js', + code: moduleExportsEntryAppNotAurelia, + }), errors: [ { message: From 08c5e008c107ff2ee3d5424160d09fe3c500be3c Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 10:39:16 +0930 Subject: [PATCH 07/43] refactor(rule/webpack-entry-point): use moduleExportsAsFunction --- test/rules/webpack-entry-point.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js index 8b881ad..5e752ff 100644 --- a/test/rules/webpack-entry-point.spec.js +++ b/test/rules/webpack-entry-point.spec.js @@ -3,11 +3,11 @@ import rule from '../../src/rules/webpack-entry-point'; const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); -const moduleExportsEntryAppNotAurelia = ` +const moduleExportsAsFunction = ({ app }) => ` module.exports = function () { return { entry: { - app: ['not-aurelia-bootstrapper'] + ${app ? `app: ['${app}']` : ''} }, } }; @@ -26,7 +26,7 @@ ruleTester.run('webpack-entry-point', rule, { // Only webpack.config.js is checked for valid entry.app values ...embedFilenameInCode({ filename: '/some/dir/not.webpack.config.js', - code: moduleExportsEntryAppNotAurelia, + code: moduleExportsAsFunction({ app: 'not-aurelia-bootstrapper' }), }), }, ], @@ -34,7 +34,7 @@ ruleTester.run('webpack-entry-point', rule, { { ...embedFilenameInCode({ filename: '/some/dir/webpack.config.js', - code: moduleExportsEntryAppNotAurelia, + code: moduleExportsAsFunction({ app: 'not-aurelia-bootstrapper' }), }), errors: [ { From c6b7e3186762bce6a46fc88e4b528247989df43d Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 10:58:10 +0930 Subject: [PATCH 08/43] fix(rule/webpack-entry-point): checks entry.app is 'aurelia-bootstrapper' --- src/rules/webpack-entry-point.js | 3 ++- test/rules/webpack-entry-point.spec.js | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js index 7a83f6f..e2decc3 100644 --- a/src/rules/webpack-entry-point.js +++ b/src/rules/webpack-entry-point.js @@ -190,7 +190,8 @@ const webpackEntryPointIsAureliaBootrap = context => node => { return; } const elements = value.elements; - if (elements.length !== 1 || elements[0] !== 'aurelia-bootstrapper') { + + if (elements.length !== 1 || elements[0].value !== 'aurelia-bootstrapper') { context.report({ node, message: diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js index 5e752ff..b854b8e 100644 --- a/test/rules/webpack-entry-point.spec.js +++ b/test/rules/webpack-entry-point.spec.js @@ -22,6 +22,12 @@ const embedFilenameInCode = ({ filename, code }) => ({ // into an AST for use here. ruleTester.run('webpack-entry-point', rule, { valid: [ + { + ...embedFilenameInCode({ + filename: '/some/dir/webpack.config.js', + code: moduleExportsAsFunction({ app: 'aurelia-bootstrapper' }), + }), + }, { // Only webpack.config.js is checked for valid entry.app values ...embedFilenameInCode({ From e35bd32769af5ef2d875ce4de04ec15de6177ec1 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 11:01:26 +0930 Subject: [PATCH 09/43] feat(rule/webpack-entry-point): checks arrow functions --- src/rules/webpack-entry-point.js | 51 +++++++++++++++++--------- test/rules/webpack-entry-point.spec.js | 31 +++++++++++++--- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js index e2decc3..06ad04c 100644 --- a/src/rules/webpack-entry-point.js +++ b/src/rules/webpack-entry-point.js @@ -19,6 +19,7 @@ const webpackConfigFileName = 'webpack.config.js'; const types = { ArrayExpression: 'ArrayExpression', + ArrowFunctionExpression: 'ArrowFunctionExpression', AssignmentExpression: 'AssignmentExpression', BlockStatement: 'BlockStatement', ExpressionStatement: 'ExpressionStatement', @@ -79,32 +80,48 @@ const isAssignedToModuleExports = context => { // TODO Handle direct assignment rather than functions // TODO Handle arrow functions with body - // TODO Handle arrow functions with ObjectExpressions - const returnStatement = ancestors.pop(); - if (!returnStatement || returnStatement.type !== types.ReturnStatement) { + const returnStatementOrArrowFunctionExpression = ancestors.pop(); + if (!returnStatementOrArrowFunctionExpression) { // TODO: Remove Console // eslint-disable-next-line no-console - console.log('Ignoring returnStatement', returnStatement); + console.log( + 'Ignoring returnStatementOrArrowFunctionExpression', + returnStatementOrArrowFunctionExpression + ); return false; } - const blockStatement = ancestors.pop(); - if (!blockStatement || blockStatement.type !== types.BlockStatement) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring blockStatement', blockStatement); - return false; - } + if (returnStatementOrArrowFunctionExpression.type === types.ReturnStatement) { + const blockStatement = ancestors.pop(); + if (!blockStatement || blockStatement.type !== types.BlockStatement) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring blockStatement', blockStatement); + return false; + } - const functionExpression = ancestors.pop(); - if ( - !functionExpression || - functionExpression.type !== types.FunctionExpression + const functionExpression = ancestors.pop(); + if ( + !functionExpression || + functionExpression.type !== types.FunctionExpression + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring functionExpression', functionExpression); + return false; + } + } else if ( + returnStatementOrArrowFunctionExpression.type === + types.ArrowFunctionExpression ) { + // No additional tokens to consume + } else { // TODO: Remove Console // eslint-disable-next-line no-console - console.log('Ignoring functionExpression', functionExpression); - return false; + console.log( + 'Ignoring unknown type returnStatementOrArrowFunctionExpression', + returnStatementOrArrowFunctionExpression + ); } const assignmentExpression = ancestors.pop(); diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js index b854b8e..78c54eb 100644 --- a/test/rules/webpack-entry-point.spec.js +++ b/test/rules/webpack-entry-point.spec.js @@ -13,6 +13,14 @@ const moduleExportsAsFunction = ({ app }) => ` }; `; +const moduleExportsAsArrowFunction = ({ app }) => ` + module.exports = ({ production, server, extractCss, coverage, analyze, karma } = {}) => ({ + entry: { + ${app ? `app: ['${app}']` : ''} + }, + }); +`; + const embedFilenameInCode = ({ filename, code }) => ({ filename, code: `// filename=${filename}\n${code}`, @@ -23,16 +31,16 @@ const embedFilenameInCode = ({ filename, code }) => ({ ruleTester.run('webpack-entry-point', rule, { valid: [ { + // Only webpack.config.js is checked for valid entry.app values ...embedFilenameInCode({ - filename: '/some/dir/webpack.config.js', - code: moduleExportsAsFunction({ app: 'aurelia-bootstrapper' }), + filename: '/some/dir/not.webpack.config.js', + code: moduleExportsAsFunction({ app: 'not-aurelia-bootstrapper' }), }), }, { - // Only webpack.config.js is checked for valid entry.app values ...embedFilenameInCode({ - filename: '/some/dir/not.webpack.config.js', - code: moduleExportsAsFunction({ app: 'not-aurelia-bootstrapper' }), + filename: '/some/dir/webpack.config.js', + code: moduleExportsAsArrowFunction({ app: 'aurelia-bootstrapper' }), }), }, ], @@ -50,5 +58,18 @@ ruleTester.run('webpack-entry-point', rule, { }, ], }, + { + ...embedFilenameInCode({ + filename: '/some/dir/webpack.config.js', + code: moduleExportsAsArrowFunction({ app: 'not-aurelia-bootstrapper' }), + }), + errors: [ + { + message: + "entry.app must be ['aurelia-bootstrapper']: found ['not-aurelia-bootstrapper']", + type: 'Identifier', + }, + ], + }, ], }); From 06d62fe5f671f276f3a774a2caf3ecd1f572edf6 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 12:28:37 +0930 Subject: [PATCH 10/43] refactor(rule/webpack-entry-point): extract eslint-types.js --- src/rules/eslint-types.js | 16 ++++++++++++++++ src/rules/webpack-entry-point.js | 16 +--------------- 2 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 src/rules/eslint-types.js diff --git a/src/rules/eslint-types.js b/src/rules/eslint-types.js new file mode 100644 index 0000000..bdaa517 --- /dev/null +++ b/src/rules/eslint-types.js @@ -0,0 +1,16 @@ +module.exports = { + types: { + ArrayExpression: 'ArrayExpression', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AssignmentExpression: 'AssignmentExpression', + BlockStatement: 'BlockStatement', + CallExpression: 'CallExpression', + ExpressionStatement: 'ExpressionStatement', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + MemberExpression: 'MemberExpression', + ObjectExpression: 'ObjectExpression', + Property: 'Property', + ReturnStatement: 'ReturnStatement', + }, +}; diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js index 06ad04c..95d9985 100644 --- a/src/rules/webpack-entry-point.js +++ b/src/rules/webpack-entry-point.js @@ -1,5 +1,5 @@ const path = require('path'); - +const { types } = require('./eslint-types'); /* Ensure that the `webpack.config.js` file correctly specifies the entry point as `aurelia-bootstrapper`. @@ -17,20 +17,6 @@ const path = require('path'); const webpackConfigFileName = 'webpack.config.js'; -const types = { - ArrayExpression: 'ArrayExpression', - ArrowFunctionExpression: 'ArrowFunctionExpression', - AssignmentExpression: 'AssignmentExpression', - BlockStatement: 'BlockStatement', - ExpressionStatement: 'ExpressionStatement', - FunctionExpression: 'FunctionExpression', - Identifier: 'Identifier', - MemberExpression: 'MemberExpression', - ObjectExpression: 'ObjectExpression', - Property: 'Property', - ReturnStatement: 'ReturnStatement', -}; - const isAssignedToModuleExports = context => { const ancestors = context.getAncestors(); From 6946f299588a4dd9ff781b58327c21632ab50486 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 12:37:31 +0930 Subject: [PATCH 11/43] feat(rule/platform-modulename): check use.globalResources --- src/rules/platform-modulename.js | 91 ++++++++++++++++++++++++++ test/rules/platform-modulename.spec.js | 32 +++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/rules/platform-modulename.js create mode 100644 test/rules/platform-modulename.spec.js diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js new file mode 100644 index 0000000..e1160ff --- /dev/null +++ b/src/rules/platform-modulename.js @@ -0,0 +1,91 @@ +/* + Ensure that module usage is wrapped in `PLATFORM.moduleName()`. +*/ + +const { types } = require('./eslint-types'); + +const calleeObjectIsAureliaUse = node => { + if (node.type !== types.MemberExpression) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring node', node); + + return false; + } + + const useObject = node.property; + + if (useObject.type !== types.Identifier && useObject.name !== 'use') { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring useObject', useObject); + + return false; + } + + return true; +}; + +const checkGlobalResources = context => node => { + // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources + const nodeArguments = node.arguments; + + // TODO: Handle non-array arguments + if ( + nodeArguments.length !== 1 && + nodeArguments[0].type === types.ArrayExpression + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring arguments', arguments); + + return false; + } + + const elements = nodeArguments[0].elements; + elements + .filter( + element => + element.type !== types.CallExpression || + (element.callee.object.name !== 'PLATFORM' && + element.callee.property.name !== 'moduleName') + ) + .map(element => + context.report( + element, + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'" + ) + ); +}; + +const usesPlatformModuleName = context => node => { + const callee = node.callee; + + if ( + callee.property.name === 'globalResources' && + calleeObjectIsAureliaUse(callee) + ) { + checkGlobalResources(context)(node); + return; + } + + return; +}; + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'TODO', + category: 'aurelia', + fixable: 'code', + }, + }, + create: context => { + // state here + + return { + CallExpression: usesPlatformModuleName(context), + }; + }, +}; diff --git a/test/rules/platform-modulename.spec.js b/test/rules/platform-modulename.spec.js new file mode 100644 index 0000000..1adbf78 --- /dev/null +++ b/test/rules/platform-modulename.spec.js @@ -0,0 +1,32 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + ` + aurelia.use.globalResources([ + PLATFORM.moduleName('./my-custom-element') // OK + ])`, + ], + invalid: [ + { + code: ` + aurelia.use.globalResources([ + './my-custom-element' // WRONG + ])`, + errors: [ + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + }, + ], + }, + ], +}); From e97a30e2e73aa24002d9d17e75c92fdd92fea3b6 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 12:47:07 +0930 Subject: [PATCH 12/43] test(rule/platform-module): multi-length arrays Also includes line and column checking in the errors. --- test/rules/platform-modulename.spec.js | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/rules/platform-modulename.spec.js b/test/rules/platform-modulename.spec.js index 1adbf78..7f124f0 100644 --- a/test/rules/platform-modulename.spec.js +++ b/test/rules/platform-modulename.spec.js @@ -12,6 +12,12 @@ ruleTester.run('platform-modulename', rule, { ` aurelia.use.globalResources([ PLATFORM.moduleName('./my-custom-element') // OK + ])`, + ` + aurelia.use.globalResources([ + PLATFORM.moduleName('./my-custom-element1'), // OK + PLATFORM.moduleName('./my-custom-element2'), // OK + PLATFORM.moduleName('./my-custom-element3'), // OK ])`, ], invalid: [ @@ -25,6 +31,56 @@ ruleTester.run('platform-modulename', rule, { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', + line: 3, + column: 5, + }, + ], + }, + { + code: ` + aurelia.use.globalResources([ + PLATFORM.moduleName('./my-custom-element1'), // OK + './my-custom-element2', // WRONG + PLATFORM.moduleName('./my-custom-element3'), // OK + ])`, + errors: [ + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 4, + column: 5, + }, + ], + }, + { + code: ` + aurelia.use.globalResources([ + './my-custom-element1', // WRONG + './my-custom-element2', // WRONG + './my-custom-element3', // WRONG + ])`, + errors: [ + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 3, + column: 5, + }, + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 4, + column: 5, + }, + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 5, + column: 5, }, ], }, From 0d00283321d75dd17e88816fe6eebb92db4d6188 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 14:10:20 +0930 Subject: [PATCH 13/43] test(rule/platform-module): allow non-array usage --- src/rules/eslint-types.js | 1 + src/rules/platform-modulename.js | 32 ++++++++++++++------------ test/rules/platform-modulename.spec.js | 19 +++++++++++++++ 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/rules/eslint-types.js b/src/rules/eslint-types.js index bdaa517..5e8da3f 100644 --- a/src/rules/eslint-types.js +++ b/src/rules/eslint-types.js @@ -8,6 +8,7 @@ module.exports = { ExpressionStatement: 'ExpressionStatement', FunctionExpression: 'FunctionExpression', Identifier: 'Identifier', + Literal: 'Literal', MemberExpression: 'MemberExpression', ObjectExpression: 'ObjectExpression', Property: 'Property', diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index e1160ff..b469092 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -26,33 +26,35 @@ const calleeObjectIsAureliaUse = node => { return true; }; -const checkGlobalResources = context => node => { +const checkGlobalResources = context => callExpression => { // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources - const nodeArguments = node.arguments; + const callExpressionArguments = callExpression.arguments; // TODO: Handle non-array arguments - if ( - nodeArguments.length !== 1 && - nodeArguments[0].type === types.ArrayExpression - ) { + if (callExpressionArguments.length !== 1) { // TODO: Remove Console // eslint-disable-next-line no-console - console.log('Ignoring arguments', arguments); + console.log('Ignoring incorrect number of arguments', arguments); return false; } - const elements = nodeArguments[0].elements; - elements + const arg = callExpressionArguments[0]; + let globalResources = [arg]; // unify to array: use.globalResource(resource) => use.globalResource([resource]) + if (arg.type === types.ArrayExpression) { + globalResources = arg.elements; + } + + globalResources .filter( - element => - element.type !== types.CallExpression || - (element.callee.object.name !== 'PLATFORM' && - element.callee.property.name !== 'moduleName') + globalResource => + globalResource.type !== types.CallExpression || + (globalResource.callee.object.name !== 'PLATFORM' && + globalResource.callee.property.name !== 'moduleName') ) - .map(element => + .map(globalResource => context.report( - element, + globalResource, "use.globalResources must wrap modules with 'PLATFORM.moduleName()'" ) ); diff --git a/test/rules/platform-modulename.spec.js b/test/rules/platform-modulename.spec.js index 7f124f0..48dbb04 100644 --- a/test/rules/platform-modulename.spec.js +++ b/test/rules/platform-modulename.spec.js @@ -10,6 +10,10 @@ const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); ruleTester.run('platform-modulename', rule, { valid: [ ` + aurelia.use.globalResources( + PLATFORM.moduleName('./my-custom-element') // OK + )`, + ` aurelia.use.globalResources([ PLATFORM.moduleName('./my-custom-element') // OK ])`, @@ -23,6 +27,21 @@ ruleTester.run('platform-modulename', rule, { invalid: [ { code: ` + aurelia.use.globalResources( + './my-custom-element' // WRONG + )`, + errors: [ + { + message: + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 3, + column: 5, + }, + ], + }, + { + code: ` aurelia.use.globalResources([ './my-custom-element' // WRONG ])`, From d6ce01b09a674009be0424a0d4ee5eebecdb8c54 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 15:38:27 +0930 Subject: [PATCH 14/43] test(rule/platform-module): feature modules are not checked --- src/rules/platform-modulename.js | 19 +++++++++++++------ test/rules/platform-modulename.spec.js | 14 +++++++++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index b469092..a64a14b 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -4,21 +4,28 @@ const { types } = require('./eslint-types'); -const calleeObjectIsAureliaUse = node => { - if (node.type !== types.MemberExpression) { +const calleeObjectIsAureliaUse = callee => { + if (callee.type !== types.MemberExpression) { // TODO: Remove Console // eslint-disable-next-line no-console - console.log('Ignoring node', node); + console.log('Ignoring callee', callee); return false; } - const useObject = node.property; + const object = callee.object; + if (!object) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring undefined object in callee', callee); + + return false; + } - if (useObject.type !== types.Identifier && useObject.name !== 'use') { + if (object.type === types.Identifier && object.name !== 'use') { // TODO: Remove Console // eslint-disable-next-line no-console - console.log('Ignoring useObject', useObject); + console.log('Ignoring object', object); return false; } diff --git a/test/rules/platform-modulename.spec.js b/test/rules/platform-modulename.spec.js index 48dbb04..753ca26 100644 --- a/test/rules/platform-modulename.spec.js +++ b/test/rules/platform-modulename.spec.js @@ -7,7 +7,7 @@ const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. -ruleTester.run('platform-modulename', rule, { +ruleTester.run('platform-modulename :: use.globalResources', rule, { valid: [ ` aurelia.use.globalResources( @@ -105,3 +105,15 @@ ruleTester.run('platform-modulename', rule, { }, ], }); + +ruleTester.run('platform-modulename :: Feature Module', rule, { + valid: [ + ` // A Feature Module index.js + export function configure(config) { + // Doesn't use PLATFORM.module() + config.globalResources(['./my-component', './my-component-2', 'my-component-3', 'etc.']); + } +`, + ], + invalid: [], +}); From 2ef5cafe7858b6d35229c197cc36d3cde02721af Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 15:53:24 +0930 Subject: [PATCH 15/43] refactor(rule/platform-modulename): separate into multiple test files https://aurelia.io/docs/build-systems/webpack/a-basic-example#platformmodulename Has different places the rule will apply, separating these checks makes testing these simpler. --- .../platform-modulename-featureModule.spec.js | 20 +++++++++++++++++++ ...orm-modulename-useGlobalResources.spec.js} | 14 +------------ 2 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 test/rules/platform-modulename-featureModule.spec.js rename test/rules/{platform-modulename.spec.js => platform-modulename-useGlobalResources.spec.js} (86%) diff --git a/test/rules/platform-modulename-featureModule.spec.js b/test/rules/platform-modulename-featureModule.spec.js new file mode 100644 index 0000000..11c7152 --- /dev/null +++ b/test/rules/platform-modulename-featureModule.spec.js @@ -0,0 +1,20 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + ` // A Feature Module index.js + export function configure(config) { + // Doesn't use PLATFORM.module() + config.globalResources(['./my-component', './my-component-2', 'my-component-3', 'etc.']); + } +`, + ], + invalid: [], +}); diff --git a/test/rules/platform-modulename.spec.js b/test/rules/platform-modulename-useGlobalResources.spec.js similarity index 86% rename from test/rules/platform-modulename.spec.js rename to test/rules/platform-modulename-useGlobalResources.spec.js index 753ca26..48dbb04 100644 --- a/test/rules/platform-modulename.spec.js +++ b/test/rules/platform-modulename-useGlobalResources.spec.js @@ -7,7 +7,7 @@ const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. -ruleTester.run('platform-modulename :: use.globalResources', rule, { +ruleTester.run('platform-modulename', rule, { valid: [ ` aurelia.use.globalResources( @@ -105,15 +105,3 @@ ruleTester.run('platform-modulename :: use.globalResources', rule, { }, ], }); - -ruleTester.run('platform-modulename :: Feature Module', rule, { - valid: [ - ` // A Feature Module index.js - export function configure(config) { - // Doesn't use PLATFORM.module() - config.globalResources(['./my-component', './my-component-2', 'my-component-3', 'etc.']); - } -`, - ], - invalid: [], -}); From 55da55334f3af3e9aeb87017dbafd7490300d889 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 16:08:41 +0930 Subject: [PATCH 16/43] refactor(rule/platform-modulename): inline methods context is going to be necessary, along with state. Without mucking around with classes inlining is the easiest way to close over that state. --- src/rules/platform-modulename.js | 156 +++++++++++++++---------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index a64a14b..7b144ba 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -4,83 +4,6 @@ const { types } = require('./eslint-types'); -const calleeObjectIsAureliaUse = callee => { - if (callee.type !== types.MemberExpression) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring callee', callee); - - return false; - } - - const object = callee.object; - if (!object) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring undefined object in callee', callee); - - return false; - } - - if (object.type === types.Identifier && object.name !== 'use') { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring object', object); - - return false; - } - - return true; -}; - -const checkGlobalResources = context => callExpression => { - // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources - const callExpressionArguments = callExpression.arguments; - - // TODO: Handle non-array arguments - if (callExpressionArguments.length !== 1) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring incorrect number of arguments', arguments); - - return false; - } - - const arg = callExpressionArguments[0]; - let globalResources = [arg]; // unify to array: use.globalResource(resource) => use.globalResource([resource]) - if (arg.type === types.ArrayExpression) { - globalResources = arg.elements; - } - - globalResources - .filter( - globalResource => - globalResource.type !== types.CallExpression || - (globalResource.callee.object.name !== 'PLATFORM' && - globalResource.callee.property.name !== 'moduleName') - ) - .map(globalResource => - context.report( - globalResource, - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'" - ) - ); -}; - -const usesPlatformModuleName = context => node => { - const callee = node.callee; - - if ( - callee.property.name === 'globalResources' && - calleeObjectIsAureliaUse(callee) - ) { - checkGlobalResources(context)(node); - return; - } - - return; -}; - module.exports = { meta: { type: 'problem', @@ -93,8 +16,85 @@ module.exports = { create: context => { // state here + const calleeObjectIsAureliaUse = callee => { + if (callee.type !== types.MemberExpression) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring callee', callee); + + return false; + } + + const object = callee.object; + if (!object) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring undefined object in callee', callee); + + return false; + } + + if (object.type === types.Identifier && object.name !== 'use') { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring object', object); + + return false; + } + + return true; + }; + + const checkGlobalResources = callExpression => { + // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources + const callExpressionArguments = callExpression.arguments; + + // TODO: Handle non-array arguments + if (callExpressionArguments.length !== 1) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring incorrect number of arguments', arguments); + + return false; + } + + const arg = callExpressionArguments[0]; + let globalResources = [arg]; // unify to array: use.globalResource(resource) => use.globalResource([resource]) + if (arg.type === types.ArrayExpression) { + globalResources = arg.elements; + } + + globalResources + .filter( + globalResource => + globalResource.type !== types.CallExpression || + (globalResource.callee.object.name !== 'PLATFORM' && + globalResource.callee.property.name !== 'moduleName') + ) + .map(globalResource => + context.report( + globalResource, + "use.globalResources must wrap modules with 'PLATFORM.moduleName()'" + ) + ); + }; + + const usesPlatformModuleName = node => { + const callee = node.callee; + + if ( + callee.property.name === 'globalResources' && + calleeObjectIsAureliaUse(callee) + ) { + checkGlobalResources(node); + return; + } + + return; + }; + return { - CallExpression: usesPlatformModuleName(context), + CallExpression: usesPlatformModuleName, }; }, }; From d2a735805fc2293489504e0468d2c85746c44d3c Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 17:01:25 +0930 Subject: [PATCH 17/43] test(rule/platform-modulename): check setRoot This adds correct handling of exported configure function and checking the parameter name aurelia matches. --- src/rules/eslint-types.js | 1 + src/rules/platform-modulename.js | 114 +++++++++++++++--- .../rules/platform-modulename-setRoot.spec.js | 35 ++++++ 3 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 test/rules/platform-modulename-setRoot.spec.js diff --git a/src/rules/eslint-types.js b/src/rules/eslint-types.js index 5e8da3f..0163f5e 100644 --- a/src/rules/eslint-types.js +++ b/src/rules/eslint-types.js @@ -6,6 +6,7 @@ module.exports = { BlockStatement: 'BlockStatement', CallExpression: 'CallExpression', ExpressionStatement: 'ExpressionStatement', + FunctionDeclaration: 'FunctionDeclaration', FunctionExpression: 'FunctionExpression', Identifier: 'Identifier', Literal: 'Literal', diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 7b144ba..94e859d 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -14,7 +14,74 @@ module.exports = { }, }, create: context => { - // state here + let aureliaParameter; + + const captureAureliaConfigure = exportNamedDeclaration => { + const functionDeclaration = exportNamedDeclaration.declaration; + if (functionDeclaration.type !== types.FunctionDeclaration) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring exportNamedDeclaration', exportNamedDeclaration); + + return; + } + + if ( + !functionDeclaration.id || + functionDeclaration.id.name !== 'configure' + ) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring non "configure" function', functionDeclaration); + + return; + } + + const params = functionDeclaration.params; + if (params.length !== 1) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log( + 'Ignoring "configure" function with incorrect params', + functionDeclaration + ); + + return; + } + + aureliaParameter = params[0]; + }; + + const calleeObjectIsAurelia = callee => { + if (callee.type !== types.MemberExpression) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring callee', callee); + + return false; + } + + if (!aureliaParameter) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log( + 'Ignoring calleeObjectIsAurelia check, aureliaParameter is not set', + callee + ); + + return false; + } + + if (aureliaParameter.name !== callee.object.name) { + // TODO: Remove Console + // eslint-disable-next-line no-console + console.log('Ignoring callee as does not use aurelia', callee); + + return false; + } + + return true; + }; const calleeObjectIsAureliaUse = callee => { if (callee.type !== types.MemberExpression) { @@ -45,19 +112,21 @@ module.exports = { return true; }; + const nodeIsCallToPlatformModuleName = node => + node.type === types.CallExpression && + node.callee.object.name === 'PLATFORM' && + node.callee.property.name === 'moduleName'; + + const reportMustWrapModules = call => node => + context.report( + node, + `${call} must wrap modules with 'PLATFORM.moduleName()'` + ); + const checkGlobalResources = callExpression => { // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources const callExpressionArguments = callExpression.arguments; - // TODO: Handle non-array arguments - if (callExpressionArguments.length !== 1) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring incorrect number of arguments', arguments); - - return false; - } - const arg = callExpressionArguments[0]; let globalResources = [arg]; // unify to array: use.globalResource(resource) => use.globalResource([resource]) if (arg.type === types.ArrayExpression) { @@ -66,17 +135,9 @@ module.exports = { globalResources .filter( - globalResource => - globalResource.type !== types.CallExpression || - (globalResource.callee.object.name !== 'PLATFORM' && - globalResource.callee.property.name !== 'moduleName') + globalResource => !nodeIsCallToPlatformModuleName(globalResource) ) - .map(globalResource => - context.report( - globalResource, - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'" - ) - ); + .map(reportMustWrapModules('use.globalResources')); }; const usesPlatformModuleName = node => { @@ -84,16 +145,29 @@ module.exports = { if ( callee.property.name === 'globalResources' && + node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { checkGlobalResources(node); return; } + if ( + callee.property.name === 'setRoot' && + node.arguments.length === 1 && + calleeObjectIsAurelia(callee) + ) { + const arg = node.arguments[0]; + if (!nodeIsCallToPlatformModuleName(arg)) { + reportMustWrapModules('setRoot')(arg); + } + } + return; }; return { + ExportNamedDeclaration: captureAureliaConfigure, CallExpression: usesPlatformModuleName, }; }, diff --git a/test/rules/platform-modulename-setRoot.spec.js b/test/rules/platform-modulename-setRoot.spec.js new file mode 100644 index 0000000..b86ba31 --- /dev/null +++ b/test/rules/platform-modulename-setRoot.spec.js @@ -0,0 +1,35 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + ` +export function configure(aurelia) { + aurelia.setRoot(PLATFORM.moduleName('app')) // OK +} +`, + ], + invalid: [ + { + code: ` +export function configure(aurelia) { + aurelia.setRoot('app') // WRONG +} +`, + errors: [ + { + message: "setRoot must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 3, + column: 19, + }, + ], + }, + ], +}); From 3499568c915a0e931ce00b009655d94201289f57 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Thu, 18 Jul 2019 17:11:52 +0930 Subject: [PATCH 18/43] test(rule/platform-modulename): check setRoot --- test/rules/platform-modulename-setRoot.spec.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/rules/platform-modulename-setRoot.spec.js b/test/rules/platform-modulename-setRoot.spec.js index b86ba31..4fc0d43 100644 --- a/test/rules/platform-modulename-setRoot.spec.js +++ b/test/rules/platform-modulename-setRoot.spec.js @@ -13,6 +13,16 @@ ruleTester.run('platform-modulename', rule, { export function configure(aurelia) { aurelia.setRoot(PLATFORM.moduleName('app')) // OK } +`, + ` +export function configure(aurelia) { + // ignored as not calling on aurelia parameter + notAurelia.setRoot('app') // OK +} +`, + ` + // ignored as not calling from within configure + aurelia.setRoot('app') // OK `, ], invalid: [ From e6d657cde298649f3eb38ea48ae3784dcafa823b Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 09:21:17 +0930 Subject: [PATCH 19/43] fix(rule/platform-modulename): set description --- src/rules/platform-modulename.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 94e859d..2b6c9bd 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -8,7 +8,7 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'TODO', + description: 'Enforce wrapping of module name in PLATFORM.moduleName()', category: 'aurelia', fixable: 'code', }, From 276296073dfadd368e486676c37e1ff646e5af4f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 09:21:44 +0930 Subject: [PATCH 20/43] fix(rule/platform-modulename): fixable --- src/rules/platform-modulename.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 2b6c9bd..c2350e5 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -10,8 +10,8 @@ module.exports = { docs: { description: 'Enforce wrapping of module name in PLATFORM.moduleName()', category: 'aurelia', - fixable: 'code', }, + fixable: 'code', }, create: context => { let aureliaParameter; From 6407fe9788271e6a7a21f9c8414347aa303e4447 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 09:27:09 +0930 Subject: [PATCH 21/43] feat(rule/platform-modulename): add optional debug property to rule Enabling turns on debug output for the rule. --- src/rules/platform-modulename.js | 65 +++++++++------- ...form-modulename-useGlobalResources.spec.js | 75 ++++++++++++++----- 2 files changed, 91 insertions(+), 49 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index c2350e5..8cc4cc5 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -12,16 +12,36 @@ module.exports = { category: 'aurelia', }, fixable: 'code', + schema: [ + { + type: 'object', + properties: { + debug: { + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], }, create: context => { let aureliaParameter; + let isDebugEnabled = false; + + if (context.options && context.options.length >= 1) { + isDebugEnabled = context.options[0].debug; + } + + const logDebug = (...args) => { + if (isDebugEnabled) { + console.log('DEBUG: ', args); + } + }; const captureAureliaConfigure = exportNamedDeclaration => { const functionDeclaration = exportNamedDeclaration.declaration; if (functionDeclaration.type !== types.FunctionDeclaration) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring exportNamedDeclaration', exportNamedDeclaration); + logDebug('Ignoring exportNamedDeclaration', exportNamedDeclaration); return; } @@ -30,18 +50,14 @@ module.exports = { !functionDeclaration.id || functionDeclaration.id.name !== 'configure' ) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring non "configure" function', functionDeclaration); + logDebug('Ignoring non "configure" function', functionDeclaration); return; } const params = functionDeclaration.params; if (params.length !== 1) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log( + logDebug( 'Ignoring "configure" function with incorrect params', functionDeclaration ); @@ -54,17 +70,13 @@ module.exports = { const calleeObjectIsAurelia = callee => { if (callee.type !== types.MemberExpression) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring callee', callee); + logDebug('Ignoring callee', callee); return false; } if (!aureliaParameter) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log( + logDebug( 'Ignoring calleeObjectIsAurelia check, aureliaParameter is not set', callee ); @@ -73,39 +85,34 @@ module.exports = { } if (aureliaParameter.name !== callee.object.name) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring callee as does not use aurelia', callee); + logDebug('aurelia parameter=', aureliaParameter); + + logDebug( + 'Ignoring callee as does not use aurelia', + aureliaParameter, + callee + ); return false; } - return true; }; const calleeObjectIsAureliaUse = callee => { if (callee.type !== types.MemberExpression) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring callee', callee); + logDebug('Ignoring callee', callee); return false; } const object = callee.object; if (!object) { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring undefined object in callee', callee); + logDebug('Ignoring property', property); return false; } if (object.type === types.Identifier && object.name !== 'use') { - // TODO: Remove Console - // eslint-disable-next-line no-console - console.log('Ignoring object', object); - return false; } diff --git a/test/rules/platform-modulename-useGlobalResources.spec.js b/test/rules/platform-modulename-useGlobalResources.spec.js index 48dbb04..494c79e 100644 --- a/test/rules/platform-modulename-useGlobalResources.spec.js +++ b/test/rules/platform-modulename-useGlobalResources.spec.js @@ -5,100 +5,135 @@ import rule from '../../src/rules/platform-modulename'; const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); +const shouldEnableDebug = false; + // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. ruleTester.run('platform-modulename', rule, { valid: [ - ` - aurelia.use.globalResources( + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.globalResources( // single element PLATFORM.moduleName('./my-custom-element') // OK - )`, - ` - aurelia.use.globalResources([ + ) +} +`, + }, + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.globalResources([ // array of one PLATFORM.moduleName('./my-custom-element') // OK - ])`, - ` - aurelia.use.globalResources([ + ]) +} +`, + }, + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(au) { + au.use.globalResources([ PLATFORM.moduleName('./my-custom-element1'), // OK PLATFORM.moduleName('./my-custom-element2'), // OK PLATFORM.moduleName('./my-custom-element3'), // OK - ])`, + ]) +} +`, + }, ], invalid: [ { + options: [{ debug: shouldEnableDebug || true }], code: ` +export function configure(aurelia) { aurelia.use.globalResources( './my-custom-element' // WRONG - )`, + ) +} +`, errors: [ { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 3, + line: 4, column: 5, }, ], }, { + options: [{ debug: shouldEnableDebug }], code: ` +export function configure(aurelia) { aurelia.use.globalResources([ './my-custom-element' // WRONG - ])`, + ]) +} +`, errors: [ { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 3, + line: 4, column: 5, }, ], }, { + options: [{ debug: shouldEnableDebug }], code: ` - aurelia.use.globalResources([ +export function configure(au) { + au.use.globalResources([ PLATFORM.moduleName('./my-custom-element1'), // OK './my-custom-element2', // WRONG PLATFORM.moduleName('./my-custom-element3'), // OK - ])`, + ]) +} +`, errors: [ { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 4, + line: 5, column: 5, }, ], }, { + options: [{ debug: shouldEnableDebug }], code: ` +export function configure(aurelia) { aurelia.use.globalResources([ './my-custom-element1', // WRONG './my-custom-element2', // WRONG './my-custom-element3', // WRONG - ])`, + ]) +}`, errors: [ { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 3, + line: 4, column: 5, }, { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 4, + line: 5, column: 5, }, { message: "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', - line: 5, + line: 6, column: 5, }, ], From 88334e270b5c39a832561e2af81d15649eaf5708 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 09:29:00 +0930 Subject: [PATCH 22/43] fix(rule/platform-modulename): checks aurelia is used before enabling rule --- src/rules/platform-modulename.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 8cc4cc5..554adfd 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -106,13 +106,15 @@ module.exports = { } const object = callee.object; - if (!object) { + const property = object.property; + + if (property.type !== types.Identifier || property.name !== 'use') { logDebug('Ignoring property', property); return false; } - if (object.type === types.Identifier && object.name !== 'use') { + if (!calleeObjectIsAurelia(object)) { return false; } From c2ba3f9211a94cff441dfbbf018324d315e89cf7 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 10:57:02 +0930 Subject: [PATCH 23/43] fix(rule/platform-modulename): remove debug from test --- test/rules/platform-modulename-useGlobalResources.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rules/platform-modulename-useGlobalResources.spec.js b/test/rules/platform-modulename-useGlobalResources.spec.js index 494c79e..08b0f24 100644 --- a/test/rules/platform-modulename-useGlobalResources.spec.js +++ b/test/rules/platform-modulename-useGlobalResources.spec.js @@ -46,7 +46,7 @@ export function configure(au) { ], invalid: [ { - options: [{ debug: shouldEnableDebug || true }], + options: [{ debug: shouldEnableDebug }], code: ` export function configure(aurelia) { aurelia.use.globalResources( From 75034bbf59e09fc5648451593544aa58d6a400c1 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 10:58:03 +0930 Subject: [PATCH 24/43] fix(rule/platform-modulename): featureModule should not enforce checks --- src/rules/platform-modulename.js | 32 ++++++++++++++++--- .../platform-modulename-featureModule.spec.js | 7 +++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 554adfd..5a30c18 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -99,17 +99,39 @@ module.exports = { }; const calleeObjectIsAureliaUse = callee => { + // Check for calls like aurelia.use.(); if (callee.type !== types.MemberExpression) { - logDebug('Ignoring callee', callee); + logDebug( + 'calleeObjectIsAureliaUse():', + 'Ignoring callee as wrong type', + callee + ); return false; } - const object = callee.object; - const property = object.property; + const object = callee.object; // MemberExpression of call + const objectProperty = object.property; + + if (!objectProperty) { + logDebug( + 'calleeObjectIsAureliaUse():', + 'Ignoring callee as object has no property', + callee + ); - if (property.type !== types.Identifier || property.name !== 'use') { - logDebug('Ignoring property', property); + return false; + } + + if ( + objectProperty.type !== types.Identifier || + objectProperty.name !== 'use' + ) { + logDebug( + 'calleeObjectIsAureliaUse():', + 'Ignoring property as not .use', + objectProperty + ); return false; } diff --git a/test/rules/platform-modulename-featureModule.spec.js b/test/rules/platform-modulename-featureModule.spec.js index 11c7152..9fba487 100644 --- a/test/rules/platform-modulename-featureModule.spec.js +++ b/test/rules/platform-modulename-featureModule.spec.js @@ -5,16 +5,21 @@ import rule from '../../src/rules/platform-modulename'; const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); +const shouldEnableDebug = false; + // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. ruleTester.run('platform-modulename', rule, { valid: [ - ` // A Feature Module index.js + { + options: [{ debug: shouldEnableDebug || true }], + code: ` // A Feature Module index.js export function configure(config) { // Doesn't use PLATFORM.module() config.globalResources(['./my-component', './my-component-2', 'my-component-3', 'etc.']); } `, + }, ], invalid: [], }); From 3cad898aa5581b3593f9ca3d6ea8af4a93d5dd87 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 10:58:45 +0930 Subject: [PATCH 25/43] fix(rule/platform-modulename): change to spread arguments in console.log --- src/rules/platform-modulename.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 5a30c18..b8bd9c8 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -34,7 +34,7 @@ module.exports = { const logDebug = (...args) => { if (isDebugEnabled) { - console.log('DEBUG: ', args); + console.log('DEBUG:', ...args); } }; From 01a606c135484c405abdf1c31b8855ff5ff10d61 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 11:41:55 +0930 Subject: [PATCH 26/43] refactor(rule/platform-modulename): include method name in debugs --- src/rules/platform-modulename.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index b8bd9c8..3ffc593 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -41,7 +41,11 @@ module.exports = { const captureAureliaConfigure = exportNamedDeclaration => { const functionDeclaration = exportNamedDeclaration.declaration; if (functionDeclaration.type !== types.FunctionDeclaration) { - logDebug('Ignoring exportNamedDeclaration', exportNamedDeclaration); + logDebug( + 'captureAureliaConfigure():', + 'Ignoring exportNamedDeclaration', + exportNamedDeclaration + ); return; } @@ -50,7 +54,11 @@ module.exports = { !functionDeclaration.id || functionDeclaration.id.name !== 'configure' ) { - logDebug('Ignoring non "configure" function', functionDeclaration); + logDebug( + 'captureAureliaConfigure():', + 'Ignoring non "configure" function', + functionDeclaration + ); return; } @@ -58,6 +66,7 @@ module.exports = { const params = functionDeclaration.params; if (params.length !== 1) { logDebug( + 'captureAureliaConfigure():', 'Ignoring "configure" function with incorrect params', functionDeclaration ); @@ -70,13 +79,14 @@ module.exports = { const calleeObjectIsAurelia = callee => { if (callee.type !== types.MemberExpression) { - logDebug('Ignoring callee', callee); + logDebug('calleeObjectIsAurelia():', 'Ignoring callee', callee); return false; } if (!aureliaParameter) { logDebug( + 'calleeObjectIsAurelia():', 'Ignoring calleeObjectIsAurelia check, aureliaParameter is not set', callee ); @@ -85,9 +95,14 @@ module.exports = { } if (aureliaParameter.name !== callee.object.name) { - logDebug('aurelia parameter=', aureliaParameter); + logDebug( + 'calleeObjectIsAurelia():', + 'aurelia parameter=', + aureliaParameter + ); logDebug( + 'calleeObjectIsAurelia():', 'Ignoring callee as does not use aurelia', aureliaParameter, callee From e6aa966e6f3992d7075836b10240ac3ca273824f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 11:49:07 +0930 Subject: [PATCH 27/43] refactor(rule/platform-modulename): add debug options to setRoot test --- src/rules/platform-modulename.js | 6 ------ test/rules/platform-modulename-setRoot.spec.js | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 3ffc593..d874b00 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -95,12 +95,6 @@ module.exports = { } if (aureliaParameter.name !== callee.object.name) { - logDebug( - 'calleeObjectIsAurelia():', - 'aurelia parameter=', - aureliaParameter - ); - logDebug( 'calleeObjectIsAurelia():', 'Ignoring callee as does not use aurelia', diff --git a/test/rules/platform-modulename-setRoot.spec.js b/test/rules/platform-modulename-setRoot.spec.js index 4fc0d43..b16c508 100644 --- a/test/rules/platform-modulename-setRoot.spec.js +++ b/test/rules/platform-modulename-setRoot.spec.js @@ -5,28 +5,40 @@ import rule from '../../src/rules/platform-modulename'; const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); +const shouldEnableDebug = false; + // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. ruleTester.run('platform-modulename', rule, { valid: [ - ` + { + options: [{ debug: shouldEnableDebug }], + code: ` export function configure(aurelia) { aurelia.setRoot(PLATFORM.moduleName('app')) // OK } `, - ` + }, + { + options: [{ debug: shouldEnableDebug }], + code: ` export function configure(aurelia) { // ignored as not calling on aurelia parameter notAurelia.setRoot('app') // OK } `, - ` + }, + { + options: [{ debug: shouldEnableDebug }], + code: ` // ignored as not calling from within configure aurelia.setRoot('app') // OK `, + }, ], invalid: [ { + options: [{ debug: shouldEnableDebug }], code: ` export function configure(aurelia) { aurelia.setRoot('app') // WRONG From a27e315d947dce964223e6cc39a678c4c27cafa0 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 11:55:37 +0930 Subject: [PATCH 28/43] fix(rule/platform-modulename): remove hard coding of debug in test --- test/rules/platform-modulename-featureModule.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rules/platform-modulename-featureModule.spec.js b/test/rules/platform-modulename-featureModule.spec.js index 9fba487..e0a7cbd 100644 --- a/test/rules/platform-modulename-featureModule.spec.js +++ b/test/rules/platform-modulename-featureModule.spec.js @@ -12,7 +12,7 @@ const shouldEnableDebug = false; ruleTester.run('platform-modulename', rule, { valid: [ { - options: [{ debug: shouldEnableDebug || true }], + options: [{ debug: shouldEnableDebug }], code: ` // A Feature Module index.js export function configure(config) { // Doesn't use PLATFORM.module() From 2a9e07a65e91414b3900decf3b471cd61fb5a9e2 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 12:00:24 +0930 Subject: [PATCH 29/43] refactor(rule/platform-modulename): exit early after checking setRoot. --- src/rules/platform-modulename.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index d874b00..e24d262 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -201,6 +201,7 @@ module.exports = { if (!nodeIsCallToPlatformModuleName(arg)) { reportMustWrapModules('setRoot')(arg); } + return; } return; From 4e41f4423f55787924392ef3ca88bc105cb11d52 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 12:01:26 +0930 Subject: [PATCH 30/43] feat(rule/platform-modulename): check use.feature() --- src/rules/platform-modulename.js | 12 ++++++ .../rules/platform-modulename-feature.spec.js | 42 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 test/rules/platform-modulename-feature.spec.js diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index e24d262..ee60e35 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -204,6 +204,18 @@ module.exports = { return; } + if ( + callee.property.name === 'feature' && + node.arguments.length === 1 && + calleeObjectIsAureliaUse(callee) + ) { + const arg = node.arguments[0]; + if (!nodeIsCallToPlatformModuleName(arg)) { + reportMustWrapModules('use.feature')(arg); + } + return; + } + return; }; diff --git a/test/rules/platform-modulename-feature.spec.js b/test/rules/platform-modulename-feature.spec.js new file mode 100644 index 0000000..262d919 --- /dev/null +++ b/test/rules/platform-modulename-feature.spec.js @@ -0,0 +1,42 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +const shouldEnableDebug = false; + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.feature(PLATFORM.moduleName('./my-awesome-feature')) // OK +} +`, + }, + ], + + invalid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.feature('./my-awesome-feature') // WRONG +} +`, + errors: [ + { + message: "use.feature must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 3, + column: 23, + }, + ], + }, + ], +}); From 9922b2d64020c78c326811b2605571f52ab0c451 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 12:05:29 +0930 Subject: [PATCH 31/43] feat(rule/platform-modulename): check use.plugin() --- src/rules/platform-modulename.js | 12 ++++++ test/rules/platform-modulename-plugin.spec.js | 42 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 test/rules/platform-modulename-plugin.spec.js diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index ee60e35..c3ecfab 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -216,6 +216,18 @@ module.exports = { return; } + if ( + callee.property.name === 'plugin' && + node.arguments.length === 1 && + calleeObjectIsAureliaUse(callee) + ) { + const arg = node.arguments[0]; + if (!nodeIsCallToPlatformModuleName(arg)) { + reportMustWrapModules('use.plugin')(arg); + } + return; + } + return; }; diff --git a/test/rules/platform-modulename-plugin.spec.js b/test/rules/platform-modulename-plugin.spec.js new file mode 100644 index 0000000..d3373b5 --- /dev/null +++ b/test/rules/platform-modulename-plugin.spec.js @@ -0,0 +1,42 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +const shouldEnableDebug = true; + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.plugin(PLATFORM.moduleName('some-awesome-plugin')) // OK +} +`, + }, + ], + + invalid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export function configure(aurelia) { + aurelia.use.plugin('some-awesome-plugin') // WRONG +} +`, + errors: [ + { + message: "use.plugin must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 3, + column: 22, + }, + ], + }, + ], +}); From fb87bd28578cb3353e4923542b320897fbdbb311 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 16:06:55 +0930 Subject: [PATCH 32/43] fix(rule/platform-modulename): remove hard coding of debug in test --- test/rules/platform-modulename-plugin.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rules/platform-modulename-plugin.spec.js b/test/rules/platform-modulename-plugin.spec.js index d3373b5..90b49e2 100644 --- a/test/rules/platform-modulename-plugin.spec.js +++ b/test/rules/platform-modulename-plugin.spec.js @@ -5,7 +5,7 @@ import rule from '../../src/rules/platform-modulename'; const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); -const shouldEnableDebug = true; +const shouldEnableDebug = false; // Use https://astexplorer.net/ and `espree` tokens to transform your `code` // into an AST for use here. From 443e48a451209318ba4bce01d96402b342b93f3f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 16:07:33 +0930 Subject: [PATCH 33/43] refactor(rule/platform-modulename): remove excessive log debugging --- src/rules/platform-modulename.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index c3ecfab..7bd8a3b 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -41,12 +41,6 @@ module.exports = { const captureAureliaConfigure = exportNamedDeclaration => { const functionDeclaration = exportNamedDeclaration.declaration; if (functionDeclaration.type !== types.FunctionDeclaration) { - logDebug( - 'captureAureliaConfigure():', - 'Ignoring exportNamedDeclaration', - exportNamedDeclaration - ); - return; } @@ -54,12 +48,6 @@ module.exports = { !functionDeclaration.id || functionDeclaration.id.name !== 'configure' ) { - logDebug( - 'captureAureliaConfigure():', - 'Ignoring non "configure" function', - functionDeclaration - ); - return; } From ddd946374eb047f254c680c33a35e2d4a6ca0937 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 16:08:11 +0930 Subject: [PATCH 34/43] feat(rule/platform-modulename): check router config --- src/rules/eslint-types.js | 2 + src/rules/platform-modulename.js | 111 ++++++++++++++++++ .../platform-modulename-router-config.spec.js | 60 ++++++++++ 3 files changed, 173 insertions(+) create mode 100644 test/rules/platform-modulename-router-config.spec.js diff --git a/src/rules/eslint-types.js b/src/rules/eslint-types.js index 0163f5e..1ee726c 100644 --- a/src/rules/eslint-types.js +++ b/src/rules/eslint-types.js @@ -11,7 +11,9 @@ module.exports = { Identifier: 'Identifier', Literal: 'Literal', MemberExpression: 'MemberExpression', + MethodDefinition: 'MethodDefinition', ObjectExpression: 'ObjectExpression', + Program: 'Program', Property: 'Property', ReturnStatement: 'ReturnStatement', }, diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 7bd8a3b..c2d47af 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -4,6 +4,26 @@ const { types } = require('./eslint-types'); +const nameOfNode = node => { + if (node.type === types.MethodDefinition) { + return node.key.name; + } + + return undefined; +}; + +const lookupInParentHierarchy = (node, type, name) => { + let current = node.parent; + while (current.type !== types.Program) { + if (current.type === type && nameOfNode(current) === name) { + return current; + } + current = current.parent; + } + + return undefined; +}; + module.exports = { meta: { type: 'problem', @@ -141,6 +161,7 @@ module.exports = { }; const nodeIsCallToPlatformModuleName = node => + node && node.type === types.CallExpression && node.callee.object.name === 'PLATFORM' && node.callee.property.name === 'moduleName'; @@ -151,6 +172,28 @@ module.exports = { `${call} must wrap modules with 'PLATFORM.moduleName()'` ); + const checkArgumentsWrappedInPlatformModuleName = callExpression => { + const arg1 = callExpression.arguments[0]; + // Treat: single arg call and array arg call the same + const args = arg1.type === types.ArrayExpression ? arg1.elements : [arg1]; + + args + .map(objectExpression => { + if (objectExpression.type !== types.ObjectExpression) { + return undefined; + } + const moduleIdProperty = objectExpression.properties.filter( + property => property.key.name === 'moduleId' + )[0]; + if (!moduleIdProperty) { + return undefined; + } + return moduleIdProperty.value; + }) + .filter(arg => arg && !nodeIsCallToPlatformModuleName(arg)) + .map(reportMustWrapModules(callExpression.callee.property.name)); + }; + const checkGlobalResources = callExpression => { // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources const callExpressionArguments = callExpression.arguments; @@ -168,6 +211,69 @@ module.exports = { .map(reportMustWrapModules('use.globalResources')); }; + const checkRouterConfig = node => { + // https://aurelia.io/docs/api/router/interface/ConfiguresRouter/method/configureRouter + const callee = node.callee; + const calleeObject = callee.object; + + if (!calleeObject) { + logDebug( + 'checkRouterConfig():', + 'Ignoring missing callee.object', + callee + ); + return; + } + + const containedWithinMethodDefinition = lookupInParentHierarchy( + node, + types.MethodDefinition, + 'configureRouter' + ); + if (!containedWithinMethodDefinition) { + logDebug( + 'checkRouterConfig():', + 'Ignoring as not contained within MethodDefinition', + node + ); + return; + } + + const methodValue = containedWithinMethodDefinition.value; + if (methodValue.type !== types.FunctionExpression) { + logDebug( + 'checkRouterConfig():', + 'Ignoring as contained within MethodDefinition does not have FunctionExpression as value', + containedWithinMethodDefinition + ); + return; + } + + const params = methodValue.params; + if (!params && params.length !== 2) { + logDebug( + 'checkRouterConfig():', + 'Ignoring as MethodDefinition as FunctionExpression does not have correct signature', + containedWithinMethodDefinition + ); + return; + } + + if (calleeObject.name !== params[0].name) { + logDebug( + 'checkRouterConfig():', + 'Ignoring as call of .map as not on Router', + containedWithinMethodDefinition + ); + return; + } + + // https://aurelia.io/docs/api/router/class/RouterConfiguration/method/map + // https://aurelia.io/docs/api/router/interface/RouteConfig + // Either a single RouteConfig or an array of them. + checkArgumentsWrappedInPlatformModuleName(node); + }; + const usesPlatformModuleName = node => { const callee = node.callee; @@ -216,6 +322,11 @@ module.exports = { return; } + if (callee.property.name === 'map' && node.arguments.length === 1) { + checkRouterConfig(node); + return; + } + return; }; diff --git a/test/rules/platform-modulename-router-config.spec.js b/test/rules/platform-modulename-router-config.spec.js new file mode 100644 index 0000000..eced176 --- /dev/null +++ b/test/rules/platform-modulename-router-config.spec.js @@ -0,0 +1,60 @@ +// Tests for https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction + +import eslint from 'eslint'; +import rule from '../../src/rules/platform-modulename'; + +const ruleTester = new eslint.RuleTester({ parser: 'babel-eslint' }); + +const shouldEnableDebug = false; + +// Use https://astexplorer.net/ and `espree` tokens to transform your `code` +// into an AST for use here. +ruleTester.run('platform-modulename', rule, { + valid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export class MyViewModel { + + configureRouter(config, router) { + config.map([ + { + route: '', + moduleId: PLATFORM.moduleName('pages/home') // OK + } + ]) + } + +} +`, + }, + ], + + invalid: [ + { + options: [{ debug: shouldEnableDebug }], + code: ` +export class MyViewModel { + + configureRouter(config, router) { + config.map([ + { + route: '', + moduleId: 'pages/home' // WRONG + } + ]) + } + +} +`, + errors: [ + { + message: "map must wrap modules with 'PLATFORM.moduleName()'", + type: 'Literal', + line: 8, + column: 19, + }, + ], + }, + ], +}); From 1d515a385eacf01f7f96f27ae976df85fd9f97f3 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 16:27:52 +0930 Subject: [PATCH 35/43] refactor(rule/platform-modulename): to use checkArgumentsWrappedInPlatformModuleName --- src/rules/platform-modulename.js | 84 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index c2d47af..727a2de 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -4,6 +4,8 @@ const { types } = require('./eslint-types'); +const identity = x => x; + const nameOfNode = node => { if (node.type === types.MethodDefinition) { return node.key.name; @@ -172,43 +174,33 @@ module.exports = { `${call} must wrap modules with 'PLATFORM.moduleName()'` ); - const checkArgumentsWrappedInPlatformModuleName = callExpression => { + const transformRouteToModuleIdNode = routeNode => { + if (routeNode.type !== types.ObjectExpression) { + return undefined; + } + const moduleIdProperty = routeNode.properties.filter( + property => property.key.name === 'moduleId' + )[0]; + if (!moduleIdProperty) { + return undefined; + } + return moduleIdProperty.value; + }; + + const checkArgumentsWrappedInPlatformModuleName = ( + callExpression, + { transformation = identity, callName = undefined } + ) => { const arg1 = callExpression.arguments[0]; // Treat: single arg call and array arg call the same const args = arg1.type === types.ArrayExpression ? arg1.elements : [arg1]; args - .map(objectExpression => { - if (objectExpression.type !== types.ObjectExpression) { - return undefined; - } - const moduleIdProperty = objectExpression.properties.filter( - property => property.key.name === 'moduleId' - )[0]; - if (!moduleIdProperty) { - return undefined; - } - return moduleIdProperty.value; - }) + .map(transformation) .filter(arg => arg && !nodeIsCallToPlatformModuleName(arg)) - .map(reportMustWrapModules(callExpression.callee.property.name)); - }; - - const checkGlobalResources = callExpression => { - // https://aurelia.io/docs/api/framework/class/FrameworkConfiguration/method/globalResources - const callExpressionArguments = callExpression.arguments; - - const arg = callExpressionArguments[0]; - let globalResources = [arg]; // unify to array: use.globalResource(resource) => use.globalResource([resource]) - if (arg.type === types.ArrayExpression) { - globalResources = arg.elements; - } - - globalResources - .filter( - globalResource => !nodeIsCallToPlatformModuleName(globalResource) - ) - .map(reportMustWrapModules('use.globalResources')); + .map( + reportMustWrapModules(callName || callExpression.callee.property.name) + ); }; const checkRouterConfig = node => { @@ -271,7 +263,9 @@ module.exports = { // https://aurelia.io/docs/api/router/class/RouterConfiguration/method/map // https://aurelia.io/docs/api/router/interface/RouteConfig // Either a single RouteConfig or an array of them. - checkArgumentsWrappedInPlatformModuleName(node); + checkArgumentsWrappedInPlatformModuleName(node, { + transformation: transformRouteToModuleIdNode, + }); }; const usesPlatformModuleName = node => { @@ -282,7 +276,9 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - checkGlobalResources(node); + checkArgumentsWrappedInPlatformModuleName(node, { + callName: 'use.globalResources', + }); return; } @@ -291,10 +287,10 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAurelia(callee) ) { - const arg = node.arguments[0]; - if (!nodeIsCallToPlatformModuleName(arg)) { - reportMustWrapModules('setRoot')(arg); - } + checkArgumentsWrappedInPlatformModuleName(node, { + callName: 'setRoot', + }); + return; } @@ -303,10 +299,9 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - const arg = node.arguments[0]; - if (!nodeIsCallToPlatformModuleName(arg)) { - reportMustWrapModules('use.feature')(arg); - } + checkArgumentsWrappedInPlatformModuleName(node, { + callName: 'use.feature', + }); return; } @@ -315,10 +310,9 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - const arg = node.arguments[0]; - if (!nodeIsCallToPlatformModuleName(arg)) { - reportMustWrapModules('use.plugin')(arg); - } + checkArgumentsWrappedInPlatformModuleName(node, { + callName: 'use.plugin', + }); return; } From a22e7d887c5f187284d73d7e702fc908c4acedd4 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 16:51:47 +0930 Subject: [PATCH 36/43] refactor(rule/platform-modulename): checkArgumentsWrappedInPlatformModuleName to use generated call name --- src/rules/platform-modulename.js | 38 ++++++++++--------- .../rules/platform-modulename-feature.spec.js | 2 +- test/rules/platform-modulename-plugin.spec.js | 2 +- ...form-modulename-useGlobalResources.spec.js | 12 +++--- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 727a2de..32e2e79 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -6,6 +6,17 @@ const { types } = require('./eslint-types'); const identity = x => x; +const getName = node => { + if (node.type === types.Identifier) { + return node.name; + } + + if (node.type === types.MemberExpression) { + return `${getName(node.property)}`; + } + + return `getName() unknown Node type ${node.type}`; +}; const nameOfNode = node => { if (node.type === types.MethodDefinition) { return node.key.name; @@ -168,10 +179,10 @@ module.exports = { node.callee.object.name === 'PLATFORM' && node.callee.property.name === 'moduleName'; - const reportMustWrapModules = call => node => + const reportMustWrapModules = callName => node => context.report( node, - `${call} must wrap modules with 'PLATFORM.moduleName()'` + `${callName} must wrap modules with 'PLATFORM.moduleName()'` ); const transformRouteToModuleIdNode = routeNode => { @@ -189,18 +200,17 @@ module.exports = { const checkArgumentsWrappedInPlatformModuleName = ( callExpression, - { transformation = identity, callName = undefined } + { transformation = identity } ) => { const arg1 = callExpression.arguments[0]; // Treat: single arg call and array arg call the same const args = arg1.type === types.ArrayExpression ? arg1.elements : [arg1]; + const callName = getName(callExpression.callee); args .map(transformation) .filter(arg => arg && !nodeIsCallToPlatformModuleName(arg)) - .map( - reportMustWrapModules(callName || callExpression.callee.property.name) - ); + .map(reportMustWrapModules(callName)); }; const checkRouterConfig = node => { @@ -276,9 +286,7 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - checkArgumentsWrappedInPlatformModuleName(node, { - callName: 'use.globalResources', - }); + checkArgumentsWrappedInPlatformModuleName(node); return; } @@ -287,9 +295,7 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAurelia(callee) ) { - checkArgumentsWrappedInPlatformModuleName(node, { - callName: 'setRoot', - }); + checkArgumentsWrappedInPlatformModuleName(node); return; } @@ -299,9 +305,7 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - checkArgumentsWrappedInPlatformModuleName(node, { - callName: 'use.feature', - }); + checkArgumentsWrappedInPlatformModuleName(node); return; } @@ -310,9 +314,7 @@ module.exports = { node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) ) { - checkArgumentsWrappedInPlatformModuleName(node, { - callName: 'use.plugin', - }); + checkArgumentsWrappedInPlatformModuleName(node); return; } diff --git a/test/rules/platform-modulename-feature.spec.js b/test/rules/platform-modulename-feature.spec.js index 262d919..e2fa663 100644 --- a/test/rules/platform-modulename-feature.spec.js +++ b/test/rules/platform-modulename-feature.spec.js @@ -31,7 +31,7 @@ export function configure(aurelia) { `, errors: [ { - message: "use.feature must wrap modules with 'PLATFORM.moduleName()'", + message: "feature must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 3, column: 23, diff --git a/test/rules/platform-modulename-plugin.spec.js b/test/rules/platform-modulename-plugin.spec.js index 90b49e2..348d9ea 100644 --- a/test/rules/platform-modulename-plugin.spec.js +++ b/test/rules/platform-modulename-plugin.spec.js @@ -31,7 +31,7 @@ export function configure(aurelia) { `, errors: [ { - message: "use.plugin must wrap modules with 'PLATFORM.moduleName()'", + message: "plugin must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 3, column: 22, diff --git a/test/rules/platform-modulename-useGlobalResources.spec.js b/test/rules/platform-modulename-useGlobalResources.spec.js index 08b0f24..4b03e8e 100644 --- a/test/rules/platform-modulename-useGlobalResources.spec.js +++ b/test/rules/platform-modulename-useGlobalResources.spec.js @@ -57,7 +57,7 @@ export function configure(aurelia) { errors: [ { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 4, column: 5, @@ -76,7 +76,7 @@ export function configure(aurelia) { errors: [ { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 4, column: 5, @@ -97,7 +97,7 @@ export function configure(au) { errors: [ { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 5, column: 5, @@ -117,21 +117,21 @@ export function configure(aurelia) { errors: [ { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 4, column: 5, }, { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 5, column: 5, }, { message: - "use.globalResources must wrap modules with 'PLATFORM.moduleName()'", + "globalResources must wrap modules with 'PLATFORM.moduleName()'", type: 'Literal', line: 6, column: 5, From b4e2baa6cd3b942ce0803e1d47bedc37e9e50351 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 17:00:25 +0930 Subject: [PATCH 37/43] fix(rule/platform-modulename): incorrect default argument destructuring --- src/rules/platform-modulename.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 32e2e79..1d4209b 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -200,7 +200,7 @@ module.exports = { const checkArgumentsWrappedInPlatformModuleName = ( callExpression, - { transformation = identity } + { transformation } = { transformation: identity } ) => { const arg1 = callExpression.arguments[0]; // Treat: single arg call and array arg call the same From eeafdc4468a869d95573a0424fc9659294b1a7df Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 19 Jul 2019 17:32:20 +0930 Subject: [PATCH 38/43] feat(rule/platform-modulename): add fix for PLATFORM.moduleName --- src/rules/platform-modulename.js | 10 ++++-- ...form-modulename-useGlobalResources.spec.js | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index 1d4209b..bae45c4 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -71,6 +71,9 @@ module.exports = { } }; + const wrapInPlatformModuleName = node => fixer => + fixer.replaceText(node, `PLATFORM.moduleName('${node.value}')`); + const captureAureliaConfigure = exportNamedDeclaration => { const functionDeclaration = exportNamedDeclaration.declaration; if (functionDeclaration.type !== types.FunctionDeclaration) { @@ -180,10 +183,11 @@ module.exports = { node.callee.property.name === 'moduleName'; const reportMustWrapModules = callName => node => - context.report( + context.report({ node, - `${callName} must wrap modules with 'PLATFORM.moduleName()'` - ); + message: `${callName} must wrap modules with 'PLATFORM.moduleName()'`, + fix: wrapInPlatformModuleName(node), + }); const transformRouteToModuleIdNode = routeNode => { if (routeNode.type !== types.ObjectExpression) { diff --git a/test/rules/platform-modulename-useGlobalResources.spec.js b/test/rules/platform-modulename-useGlobalResources.spec.js index 4b03e8e..14fb284 100644 --- a/test/rules/platform-modulename-useGlobalResources.spec.js +++ b/test/rules/platform-modulename-useGlobalResources.spec.js @@ -63,6 +63,13 @@ export function configure(aurelia) { column: 5, }, ], + output: ` +export function configure(aurelia) { + aurelia.use.globalResources( + PLATFORM.moduleName('./my-custom-element') // WRONG + ) +} +`, }, { options: [{ debug: shouldEnableDebug }], @@ -82,6 +89,13 @@ export function configure(aurelia) { column: 5, }, ], + output: ` +export function configure(aurelia) { + aurelia.use.globalResources([ + PLATFORM.moduleName('./my-custom-element') // WRONG + ]) +} +`, }, { options: [{ debug: shouldEnableDebug }], @@ -103,6 +117,15 @@ export function configure(au) { column: 5, }, ], + output: ` +export function configure(au) { + au.use.globalResources([ + PLATFORM.moduleName('./my-custom-element1'), // OK + PLATFORM.moduleName('./my-custom-element2'), // WRONG + PLATFORM.moduleName('./my-custom-element3'), // OK + ]) +} +`, }, { options: [{ debug: shouldEnableDebug }], @@ -137,6 +160,14 @@ export function configure(aurelia) { column: 5, }, ], + output: ` +export function configure(aurelia) { + aurelia.use.globalResources([ + PLATFORM.moduleName('./my-custom-element1'), // WRONG + PLATFORM.moduleName('./my-custom-element2'), // WRONG + PLATFORM.moduleName('./my-custom-element3'), // WRONG + ]) +}`, }, ], }); From 0b9e1b81046591f88b693078bb699b811a0ebb41 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Mon, 22 Jul 2019 11:57:53 +0930 Subject: [PATCH 39/43] style(rules): fix eslint error --- src/rules/platform-modulename.js | 2 +- src/rules/webpack-entry-point.js | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index bae45c4..dec83eb 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -208,7 +208,7 @@ module.exports = { ) => { const arg1 = callExpression.arguments[0]; // Treat: single arg call and array arg call the same - const args = arg1.type === types.ArrayExpression ? arg1.elements : [arg1]; + const args = arg1.type === types.ArrayExpression ? arg1.elements : [ arg1 ]; const callName = getName(callExpression.callee); args diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js index 95d9985..8c80600 100644 --- a/src/rules/webpack-entry-point.js +++ b/src/rules/webpack-entry-point.js @@ -217,11 +217,7 @@ module.exports = { fixable: 'code', }, }, - create: context => { - // state here - - return { - Identifier: webpackEntryPointIsAureliaBootrap(context), - }; - }, + create: context => ({ + Identifier: webpackEntryPointIsAureliaBootrap(context), + }), }; From dfb400f6bdea309a479ce105fcbb06e7d33f4029 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Mon, 22 Jul 2019 11:59:43 +0930 Subject: [PATCH 40/43] fix(rules): add new rules to src/index.js --- src/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.js b/src/index.js index 0bb9eea..1b7d130 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,10 @@ import injectRule from './rules/inject-matches-ctor'; import injectTypeRule from './rules/inject-type'; import noConventionsRule from './rules/no-conventions'; import noConsoleLogRule from './rules/no-console-log'; +import platformModulename from './rules/platform-modulename'; import storeUnsubscribeRule from './rules/store-unsubscribe'; import sortClassMembers, { defaultOrder } from './rules/sort-class-members'; +import webpackEntryPoint from './rules/webpack-entry-point'; module.exports = { rules: { @@ -11,8 +13,10 @@ module.exports = { 'inject-type': injectTypeRule, 'no-conventions': noConventionsRule, 'no-console-log': noConsoleLogRule, + 'platform-modulename': platformModulename, 'store-unsubscribe': storeUnsubscribeRule, 'sort-class-members': sortClassMembers, + 'webpack-entry-point': webpackEntryPoint, }, configs: { recommended: { @@ -21,10 +25,12 @@ module.exports = { 'inject-type': 0, 'no-conventions': 0, 'no-console-log': 0, + 'platform-modulename': 0, 'store-unsubscribe': 2, 'sort-class-members': [ 2, { order: defaultOrder, }], + 'webpack-entry-point': 0, }, }, }, From 1a9aa8ca5b244a323448d79d2f18aa16242e0028 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Mon, 22 Jul 2019 12:11:57 +0930 Subject: [PATCH 41/43] fix(rules/webpack-entry-point): error messages to be more eslint like --- src/rules/webpack-entry-point.js | 2 +- test/rules/webpack-entry-point.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rules/webpack-entry-point.js b/src/rules/webpack-entry-point.js index 8c80600..00f5796 100644 --- a/src/rules/webpack-entry-point.js +++ b/src/rules/webpack-entry-point.js @@ -198,7 +198,7 @@ const webpackEntryPointIsAureliaBootrap = context => node => { context.report({ node, message: - "entry.app must be ['aurelia-bootstrapper']: found {{ value }}", + "Expected entry.app to be ['aurelia-bootstrapper'] but found {{ value }}", data: { value: context.getSourceCode().getText(value), }, diff --git a/test/rules/webpack-entry-point.spec.js b/test/rules/webpack-entry-point.spec.js index 78c54eb..9b3df3b 100644 --- a/test/rules/webpack-entry-point.spec.js +++ b/test/rules/webpack-entry-point.spec.js @@ -53,7 +53,7 @@ ruleTester.run('webpack-entry-point', rule, { errors: [ { message: - "entry.app must be ['aurelia-bootstrapper']: found ['not-aurelia-bootstrapper']", + "Expected entry.app to be ['aurelia-bootstrapper'] but found ['not-aurelia-bootstrapper']", type: 'Identifier', }, ], @@ -66,7 +66,7 @@ ruleTester.run('webpack-entry-point', rule, { errors: [ { message: - "entry.app must be ['aurelia-bootstrapper']: found ['not-aurelia-bootstrapper']", + "Expected entry.app to be ['aurelia-bootstrapper'] but found ['not-aurelia-bootstrapper']", type: 'Identifier', }, ], From 6601cd908f2284b03efdd275a6e51f37799286e2 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Mon, 22 Jul 2019 12:12:48 +0930 Subject: [PATCH 42/43] style(rules): disable array-bracket-spacing to problem line --- src/rules/platform-modulename.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index dec83eb..e6f6d17 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -208,7 +208,7 @@ module.exports = { ) => { const arg1 = callExpression.arguments[0]; // Treat: single arg call and array arg call the same - const args = arg1.type === types.ArrayExpression ? arg1.elements : [ arg1 ]; + const args = arg1.type === types.ArrayExpression ? arg1.elements : [arg1]; // eslint-disable-line array-bracket-spacing const callName = getName(callExpression.callee); args From 1010abb43699462ca030b69f82444e792d157f20 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Mon, 22 Jul 2019 12:13:22 +0930 Subject: [PATCH 43/43] fix(rules/platform-modulename): check for undefined property --- src/rules/platform-modulename.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rules/platform-modulename.js b/src/rules/platform-modulename.js index e6f6d17..5a510b8 100644 --- a/src/rules/platform-modulename.js +++ b/src/rules/platform-modulename.js @@ -286,6 +286,7 @@ module.exports = { const callee = node.callee; if ( + callee.property && callee.property.name === 'globalResources' && node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) @@ -295,6 +296,7 @@ module.exports = { } if ( + callee.property && callee.property.name === 'setRoot' && node.arguments.length === 1 && calleeObjectIsAurelia(callee) @@ -305,6 +307,7 @@ module.exports = { } if ( + callee.property && callee.property.name === 'feature' && node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) @@ -314,6 +317,7 @@ module.exports = { } if ( + callee.property && callee.property.name === 'plugin' && node.arguments.length === 1 && calleeObjectIsAureliaUse(callee) @@ -322,7 +326,11 @@ module.exports = { return; } - if (callee.property.name === 'map' && node.arguments.length === 1) { + if ( + callee.property && + callee.property.name === 'map' && + node.arguments.length === 1 + ) { checkRouterConfig(node); return; }