-
Notifications
You must be signed in to change notification settings - Fork 902
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lint): Add a set of eslint rule that enforce package import rules
- Loading branch information
1 parent
e15b796
commit 87542a8
Showing
21 changed files
with
529 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Built lib files | ||
/**/modules/*/lib/** | ||
/**/*.spec.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use strict'; | ||
const { getSourceFileDetails, getImportFromNpm } = require('../utils/import-aliases'); | ||
|
||
/** | ||
* A group of rules that enforce spinnaker ES6 import alias conventions. | ||
* | ||
* Source code in a package (i.e., `core`) should not import from `@spinnaker/core` | ||
* | ||
* @version 0.1.0 | ||
* @category conventions | ||
*/ | ||
const rule = function(context) { | ||
const { ownPackage } = getSourceFileDetails(context.getFilename()); | ||
if (!ownPackage) { | ||
return {}; | ||
} | ||
|
||
return { | ||
ImportDeclaration: function(node) { | ||
if (node.source.type !== 'Literal' || !node.source.value) { | ||
return; | ||
} | ||
|
||
const importString = node.source.value; | ||
const importFromNpm = getImportFromNpm(importString); | ||
if (!importFromNpm || importFromNpm.pkg !== ownPackage) { | ||
return; | ||
} | ||
|
||
const { pkg, importPathWithSlash } = importFromNpm; | ||
const message = | ||
`Do not use ${importString} to import from ${ownPackage} from code inside ${ownPackage}. ` + | ||
` Instead, use the ${pkg} alias or a relative import`; | ||
|
||
const fix = fixer => fixer.replaceText(node.source, `'${pkg}${importPathWithSlash}'`); | ||
context.report({ fix, node, message }); | ||
}, | ||
}; | ||
}; | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: `Enforces spinnaker ES6 import conventions for package aliases`, | ||
}, | ||
fixable: 'code', | ||
}, | ||
create: rule, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
'use strict'; | ||
const { getAliasImport, getSourceFileDetails, getAllSpinnakerPackages } = require('../utils/import-aliases'); | ||
|
||
/** | ||
* A group of rules that enforce spinnaker ES6 import alias conventions. | ||
* | ||
* Source code in a package (i.e., `amazon`) should not import from a different package using an alias (i.e., `core/`) | ||
* Instead, it should import from `@spinnaker/core` | ||
* | ||
* @version 0.1.0 | ||
* @category conventions | ||
*/ | ||
const rule = function(context) { | ||
const sourceFile = context.getFilename(); | ||
const { modulesPath, ownPackage } = getSourceFileDetails(sourceFile); | ||
if (!ownPackage) { | ||
return {}; | ||
} | ||
const allSpinnakerPackages = getAllSpinnakerPackages(modulesPath); | ||
|
||
return { | ||
ImportDeclaration: function(node) { | ||
if (node.source.type !== 'Literal' || !node.source.value) { | ||
return; | ||
} | ||
|
||
const importString = node.source.value; | ||
const aliasImport = getAliasImport(allSpinnakerPackages, importString); | ||
|
||
if (!aliasImport || aliasImport.pkg === ownPackage) { | ||
return; | ||
} | ||
const { pkg } = aliasImport; | ||
const message = | ||
`Do not use an alias to import from ${pkg} from code inside ${ownPackage}.` + | ||
` Instead, use the npm package @spinnaker/${pkg}`; | ||
|
||
const fix = fixer => fixer.replaceText(node.source, `'@spinnaker/${pkg}'`); | ||
context.report({ fix, node, message }); | ||
}, | ||
}; | ||
}; | ||
|
||
const importAliasesRule = (module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: `Enforces spinnaker ES6 import conventions for package aliases`, | ||
}, | ||
fixable: 'code', | ||
}, | ||
create: rule, | ||
}); |
53 changes: 53 additions & 0 deletions
53
packages/eslint-plugin/rules/import-from-npm-not-relative.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
'use strict'; | ||
const { getSourceFileDetails, getRelativeImport } = require('../utils/import-aliases'); | ||
|
||
/** | ||
* A group of rules that enforce spinnaker ES6 import alias conventions. | ||
* | ||
* Source code in a package (i.e., `amazon`) should not import from package (i.e., `core`) using a relative path. | ||
* Instead, it should import from `@spinnaker/core` | ||
* | ||
* @version 0.1.0 | ||
* @category conventions | ||
*/ | ||
const rule = function(context) { | ||
const sourceFile = context.getFilename(); | ||
const { modulesPath, sourceDirectory, ownPackage } = getSourceFileDetails(sourceFile); | ||
if (!ownPackage) { | ||
return {}; | ||
} | ||
|
||
return { | ||
ImportDeclaration: function(node) { | ||
if (node.source.type !== 'Literal' || !node.source.value) { | ||
return; | ||
} | ||
|
||
const importString = node.source.value; | ||
const relativeImport = getRelativeImport(sourceDirectory, modulesPath, importString); | ||
|
||
if (!relativeImport || relativeImport.pkg === ownPackage) { | ||
return; | ||
} | ||
|
||
const { pkg } = relativeImport; | ||
const message = | ||
`Do not use a relative import to import from ${pkg} from code inside ${ownPackage}.` + | ||
` Instead, use the npm package @spinnaker/${pkg}`; | ||
|
||
const fix = fixer => fixer.replaceText(node.source, `'@spinnaker/${pkg}'`); | ||
context.report({ fix, node, message }); | ||
}, | ||
}; | ||
}; | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: `Enforces spinnaker ES6 import conventions for package aliases`, | ||
}, | ||
fixable: 'code', | ||
}, | ||
create: rule, | ||
}; |
65 changes: 65 additions & 0 deletions
65
packages/eslint-plugin/rules/import-relative-within-subpackage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
'use strict'; | ||
const path = require('path'); | ||
const { getAliasImport, getSourceFileDetails, getAllSpinnakerPackages } = require('../utils/import-aliases'); | ||
|
||
/** | ||
* A group of rules that enforce spinnaker ES6 import alias conventions. | ||
* | ||
* Source code in a package (i.e., `core/presentation` should not import from the same subpackage using an alias | ||
* `core/presentation`. | ||
* Instead, it should import relatively `../../path/file` | ||
* | ||
* @version 0.1.0 | ||
* @category conventions | ||
*/ | ||
const rule = function(context) { | ||
const sourceFile = context.getFilename(); | ||
const { modulesPath, ownPackage, ownSubPackage, filePath } = getSourceFileDetails(sourceFile); | ||
if (!ownPackage) { | ||
return {}; | ||
} | ||
const allSpinnakerPackages = getAllSpinnakerPackages(modulesPath); | ||
|
||
return { | ||
ImportDeclaration: function(node) { | ||
if (node.source.type !== 'Literal' || !node.source.value) { | ||
return; | ||
} | ||
|
||
const importString = node.source.value; | ||
const aliasImport = getAliasImport(allSpinnakerPackages, importString); | ||
if ( | ||
!aliasImport || | ||
aliasImport.pkg !== ownPackage || | ||
aliasImport.subPkg !== ownSubPackage || | ||
aliasImport.importPath === aliasImport.subPkg // don't handle import from 'core/subpackage' in this rule | ||
) { | ||
return; | ||
} | ||
|
||
const { pkg, subPkg, importPath } = aliasImport; | ||
|
||
const message = | ||
`Do not use an alias to import from ${pkg}/${subPkg} from code inside ${pkg}/${subPkg}.` + | ||
` Instead, use a relative import`; | ||
const fix = fixer => { | ||
const relativeDir = path.relative(path.dirname(filePath), path.dirname(importPath)) || '.'; | ||
let newPath = path.join(relativeDir, path.basename(importPath)); | ||
newPath = newPath.match(/^\.?\.\//) ? newPath : './' + newPath; | ||
return fixer.replaceText(node.source, `'${newPath}'`); | ||
}; | ||
context.report({ fix, node, message }); | ||
}, | ||
}; | ||
}; | ||
|
||
const importAliasesRule = (module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: `Enforces spinnaker ES6 import conventions for package aliases`, | ||
}, | ||
fixable: 'code', | ||
}, | ||
create: rule, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
{ | ||
"rules": { | ||
"prefer-bare-module": 2 | ||
}, | ||
"parserOptions": { | ||
"ecmaVersion": 8, | ||
|
24 changes: 24 additions & 0 deletions
24
packages/eslint-plugin/test/import-from-alias-not-npm.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
const ruleTester = require('../utils/ruleTester'); | ||
const rule = require('../rules/import-from-alias-not-npm'); | ||
|
||
ruleTester.run('import-from-alias-not-npm', rule, { | ||
valid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/amazon/package/amazon_source_file.ts', | ||
code: `import { Anything } from '@spinnaker/core';`, | ||
}, | ||
], | ||
|
||
invalid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/core/package/core_source_file.ts', | ||
code: `import { Anything } from '@spinnaker/core';`, | ||
output: `import { Anything } from 'core';`, | ||
errors: [ | ||
'Do not use @spinnaker/core to import from core from code inside core. Instead, use the core alias or a relative import', | ||
], | ||
}, | ||
], | ||
}); |
27 changes: 27 additions & 0 deletions
27
packages/eslint-plugin/test/import-from-npm-not-alias.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use strict'; | ||
const mockModule = require('../utils/mockModule'); | ||
const mock = mockModule('../utils/import-aliases'); | ||
mock.getAllSpinnakerPackages.mockImplementation(() => ['core', 'amazon', 'titus', 'docker']); | ||
|
||
const ruleTester = require('../utils/ruleTester'); | ||
const rule = require('../rules/import-from-npm-not-alias'); | ||
|
||
ruleTester.run('import-from-npm-not-alias', rule, { | ||
valid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/amazon/package/amazon_source_file.ts', | ||
code: `import { Anything } from 'amazon/otherpackage';`, | ||
}, | ||
], | ||
|
||
invalid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/amazon/package/amazon_source_file.ts', | ||
code: `import { Anything } from 'core/otherpackage';`, | ||
output: `import { Anything } from '@spinnaker/core';`, | ||
errors: [ | ||
'Do not use an alias to import from core from code inside amazon. Instead, use the npm package @spinnaker/core', | ||
], | ||
}, | ||
], | ||
}); |
24 changes: 24 additions & 0 deletions
24
packages/eslint-plugin/test/import-from-npm-not-relative.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
const ruleTester = require('../utils/ruleTester'); | ||
const rule = require('../rules/import-from-npm-not-relative'); | ||
|
||
ruleTester.run('import-from-npm-not-relative', rule, { | ||
valid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/amazon/package/amazon_source_file.ts', | ||
code: `import { Anything } from '../othersubpackage/file2';`, | ||
}, | ||
], | ||
|
||
invalid: [ | ||
{ | ||
filename: '/root/spinnaker/deck/app/scripts/modules/amazon/package/amazon_source_file.ts', | ||
code: `import { Anything } from '../../core/subpackage/file2';`, | ||
output: `import { Anything } from '@spinnaker/core';`, | ||
errors: [ | ||
'Do not use a relative import to import from core from code inside amazon. Instead, use the npm package @spinnaker/core', | ||
], | ||
}, | ||
], | ||
}); |
Oops, something went wrong.