forked from meteor/meteor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial dev on new rule for meteor 3.0
- Loading branch information
1 parent
9425d5e
commit a769fcc
Showing
12 changed files
with
14,803 additions
and
8,306 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
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,3 +1,5 @@ | ||
coverage/ | ||
.nyc_output | ||
.DS_Store | ||
node_modules | ||
/.meteor | ||
|
37 changes: 37 additions & 0 deletions
37
npm-packages/eslint-plugin-meteor/docs/rules/no-sync-mongo-methods-on-server.md
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,37 @@ | ||
# This rule checks the usage of syncronous MongoDB Methods on the Server which will stop working starting from Meteor 3.0 with the fiber removal (no-sync-mongo-methods-on-server) | ||
|
||
Please describe the origin of the rule here. | ||
|
||
|
||
## Rule Details | ||
|
||
This rule aims to... | ||
|
||
The following patterns are considered warnings: | ||
|
||
```js | ||
|
||
// fill me in | ||
|
||
``` | ||
|
||
The following patterns are not warnings: | ||
|
||
```js | ||
|
||
// fill me in | ||
|
||
``` | ||
|
||
### Options | ||
|
||
If there are any options, describe them here. Otherwise, delete this section. | ||
|
||
## When Not To Use It | ||
|
||
Give a short description of when it would be appropriate to turn off this rule. | ||
|
||
## Further Reading | ||
|
||
If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. | ||
|
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
202 changes: 202 additions & 0 deletions
202
npm-packages/eslint-plugin-meteor/lib/rules/no-sync-mongo-methods-on-server/helpers/index.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,202 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const readAndParse = require('./parse'); | ||
|
||
// TODO: the order is important | ||
// extensions of files that are compiled into js | ||
// and can import other js files. | ||
const parseableExt = ['.js', '.jsx', '.svelte', '.ts', '.tsx']; | ||
|
||
// These folders are not eagerly loaded by Meteor | ||
// TODO: check if we should only exclude some of these when | ||
// they are at the top level | ||
const notEagerlyLoadedDirs = [ | ||
'imports', | ||
'node_modules', | ||
'public', | ||
// TODO: have an option to include tests | ||
'tests', | ||
'test', | ||
'packages', | ||
'private', | ||
]; | ||
|
||
// The path will start with one of these if | ||
// it imports an app file | ||
const appFileImport = ['.', path.posix.sep, path.win32.sep]; | ||
|
||
function shouldWalk(folderPath, archList) { | ||
const basename = path.basename(folderPath); | ||
if (basename[0] === '.' || notEagerlyLoadedDirs.includes(basename)) { | ||
return false; | ||
} | ||
|
||
const parts = folderPath.split(path.sep); | ||
if (!archList.includes('server') && parts.includes('server')) { | ||
return false; | ||
} | ||
if (!archList.includes('client') && parts.includes('client')) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function findExt(filePath) { | ||
const ext = parseableExt.find((possibleExt) => { | ||
const exists = fs.existsSync(filePath + possibleExt); | ||
return exists; | ||
}); | ||
|
||
if (ext) { | ||
return filePath + ext; | ||
} | ||
|
||
// Maybe it is the index file in a folder | ||
// TODO: check if this should be before or after checking extensions | ||
try { | ||
const stat = fs.statSync(filePath); | ||
if (stat.isDirectory()) { | ||
return findExt(`${filePath}${path.sep}index`); | ||
} | ||
} catch (e) { | ||
// TODO: only ignore certain errors | ||
} | ||
} | ||
|
||
function shouldParse(filePath) { | ||
// console.log('shouldParse found filePath', filePath) | ||
|
||
const ext = path.extname(filePath); | ||
const basename = path.basename(filePath); | ||
|
||
// TODO: have an option to parse test files | ||
if ( | ||
basename.endsWith(`.app-tests${ext}`) || | ||
basename.endsWith(`.spec${ext}`) || | ||
basename.endsWith(`.test${ext}`) | ||
) { | ||
return false; | ||
} | ||
|
||
return basename[0] !== '.' && parseableExt.includes(ext); | ||
} | ||
|
||
function isMeteorPackage(importPath) { | ||
return importPath.startsWith('meteor/'); | ||
} | ||
|
||
function isNpmDependency(importPath) { | ||
return !appFileImport.includes(importPath[0]); | ||
} | ||
|
||
const handledFiles = new Set(); | ||
let cachedParsedFile; | ||
|
||
function getAbsFilePath(filePath) { | ||
// some files have no ext or are only the ext (.gitignore, .editorconfig, etc.) | ||
const existingExt = | ||
path.extname(filePath) || path.basename(filePath).startsWith('.'); | ||
if (!existingExt) { | ||
// TODO: should maybe only do this if a file doesn't exists with the given path | ||
// since we might be importing a file with no extension. | ||
const pathWithExt = findExt(filePath); | ||
if (!pathWithExt) { | ||
// console.log('unable to find ext', filePath); | ||
return pathWithExt; | ||
} | ||
|
||
return pathWithExt; | ||
} | ||
|
||
// TODO: if the file doesn't exist, we must try other extensions | ||
|
||
return filePath; | ||
} | ||
|
||
function handleFile(_filePath, appPath, onFile, cachedParsedFile) { | ||
const filePath = getAbsFilePath(_filePath); | ||
|
||
if (!shouldParse(filePath) || handledFiles.has(filePath)) { | ||
return; | ||
} | ||
|
||
handledFiles.add(filePath); | ||
|
||
const realPath = fs.realpathSync.native(filePath); | ||
if (cachedParsedFile[realPath]) { | ||
return; | ||
} | ||
const ast = readAndParse(filePath); | ||
cachedParsedFile[realPath] = true; | ||
// console.debug('Set key', realPath); | ||
|
||
const imports = readAndParse.findImports(filePath, ast, appPath); | ||
onFile({ path: filePath, ast, imports }); | ||
|
||
imports | ||
.filter( | ||
({ source }) => !isMeteorPackage(source) && !isNpmDependency(source) | ||
) | ||
.map(({ source }) => { | ||
if (source[0] === '/') { | ||
source = appPath + source; | ||
} | ||
return path.resolve(path.dirname(filePath), source); | ||
}) | ||
.forEach((importPath) => { | ||
handleFile(importPath, appPath, onFile, cachedParsedFile); | ||
}); | ||
} | ||
|
||
function handleFolder(folderPath, appPath, archList, onFile, cachedParsedFile) { | ||
const dirents = fs.readdirSync(folderPath, { withFileTypes: true }); | ||
// console.log('dirents', dirents) | ||
for (let i = 0; i < dirents.length; i += 1) { | ||
if (dirents[i].isDirectory()) { | ||
if (shouldWalk(path.resolve(folderPath, dirents[i].name), archList)) { | ||
handleFolder( | ||
path.resolve(folderPath, dirents[i].name), | ||
appPath, | ||
archList, | ||
onFile, | ||
cachedParsedFile | ||
); | ||
} | ||
} else if (dirents[i].isFile()) { | ||
const filePath = path.resolve(folderPath, dirents[i].name); | ||
handleFile(filePath, appPath, onFile, cachedParsedFile); | ||
} | ||
} | ||
} | ||
|
||
class Walker { | ||
cachedParsedFile; | ||
appPath; | ||
|
||
filePath() { | ||
return path.join(this.appPath, '.eslint-meteor-files'); | ||
} | ||
|
||
constructor(appPath) { | ||
this.appPath = appPath; | ||
this.cachedParsedFile = fs.existsSync(this.filePath()) | ||
? JSON.parse(fs.readFileSync(this.filePath())) | ||
: {}; | ||
} | ||
walkApp(archList, onFile) { | ||
handleFolder( | ||
this.appPath, | ||
this.appPath, | ||
archList, | ||
onFile, | ||
this.cachedParsedFile | ||
); | ||
fs.writeFileSync(this.filePath(), JSON.stringify(this.cachedParsedFile)); | ||
} | ||
get cachedParsedFile() { | ||
return this.cachedParsedFile; | ||
} | ||
} | ||
|
||
module.exports = { Walker }; |
Oops, something went wrong.