├── .gitignore ├── README.md ├── bin └── main.js ├── lib ├── default.js └── index.js ├── package.json └── test └── basic.js /.gitignore: -------------------------------------------------------------------------------- 1 | # dirs 2 | /.vscode/ 3 | /.nyc_output/ 4 | /dist/ 5 | /log/ 6 | /pid/ 7 | /node_modules/ 8 | /coverage/ 9 | /test/sample/repo 10 | 11 | # logs 12 | *.log 13 | *tgz 14 | /package-lock.json 15 | 16 | # sys and temp files 17 | *Thumbs.db 18 | *.DS_Store 19 | *._* 20 | .*.swp 21 | .~* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # be-strict 2 | 3 | [![NPM Version](http://img.shields.io/npm/v/be-strict.svg?style=flat)](https://www.npmjs.org/package/be-strict) 4 | [![NPM Downloads](https://img.shields.io/npm/dm/be-strict.svg?style=flat)](https://www.npmjs.org/package/be-strict) 5 | [![JS Standard Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) 6 | 7 | be strict: add 'use strict' on the top of js files if missing 8 | 9 | ## Installing 10 | 11 | ````bash 12 | npm i -g be-strict 13 | ```` 14 | 15 | ### Quick start 16 | 17 | ```bash 18 | $ cd /my-project 19 | $ be-strict 20 | ``` 21 | 22 | ``` 23 | skipped 3 files 24 | [ 25 | '/home/simone/dev/my-project/example/basic.js', 26 | '/home/simone/dev/my-project/lib/utils.js', 27 | '/home/simone/dev/my-project/settings/env.js' 28 | ] 29 | 30 | --- 31 | 32 | stricted 4 files 33 | [ '/home/simone/dev/my-project/main.js', 34 | '/home/simone/dev/my-project/example/app.js', 35 | '/home/simone/dev/my-project/example/settings/production.js' 36 | ] 37 | 38 | well done! this project is 'strict'! 39 | ``` 40 | 41 | ### Options 42 | 43 | ```bash 44 | $ be-strict --help 45 | 46 | Options: 47 | --help Show help [boolean] 48 | --version Show version number [boolean] 49 | -i, --ignore ignore dirs, default is .git and node_modules 50 | [string] [default: ".git,node_modules"] 51 | -p, --path repo path to apply strictness - default cwd [string] 52 | ``` 53 | 54 | --- 55 | 56 | ## License 57 | 58 | The MIT License (MIT) 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in all 68 | copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 76 | SOFTWARE. 77 | -------------------------------------------------------------------------------- /bin/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | const yargs = require('yargs/yargs') 6 | const lib = require('../lib') 7 | 8 | const { hideBin } = require('yargs/helpers') 9 | const argv = 10 | yargs(hideBin(process.argv)) 11 | .option('ignore', { 12 | alias: 'i', 13 | type: 'string', 14 | description: 'ignore dirs, default is .git and node_modules', 15 | default: '.git,node_modules' 16 | }) 17 | .option('path', { 18 | alias: 'p', 19 | type: 'string', 20 | description: 'repo path to apply strictness - default cwd' 21 | }) 22 | .argv 23 | 24 | ;(async () => { 25 | const _report = await lib.run(argv.path || process.cwd(), argv.ignore ? argv.ignore.split(',') : undefined) 26 | lib.log.info('\n---\n') 27 | lib.log.info('skipped', _report.skipped.length, 'files - already stricted') 28 | lib.log.info(_report.skipped) 29 | lib.log.info('\n---\n') 30 | lib.log.info('stricted', _report.done.length, 'files') 31 | lib.log.info(_report.done) 32 | lib.log.info('\n---\n') 33 | 34 | lib.log.info('\n well done! this project is \'strict\'!\n\n') 35 | })() 36 | -------------------------------------------------------------------------------- /lib/default.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const default_ = { 4 | ignore: ['.git', 'node_modules'] 5 | } 6 | 7 | module.exports = default_ 8 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const fs = require('fs').promises 5 | const default_ = require('./default') 6 | 7 | const BUFFER_SIZE = 64 8 | 9 | const lib = { 10 | log: { 11 | trace: function (...args) { 12 | // TODO option verbose console.trace(...args) 13 | }, 14 | info: function (...args) { 15 | console.log(...args) 16 | }, 17 | error: function (...args) { 18 | console.error(...args) 19 | } 20 | }, 21 | 22 | run: async function (path_, ignore = default_.ignore, base = '') { 23 | lib.log.trace('reading', path_, '...') 24 | const _files = await fs.readdir(path_) 25 | lib.log.trace(' ... found', _files.length, 'files in', path_) 26 | const _tasks = [] 27 | let done = [] 28 | let skipped = [] 29 | for (const _file of _files) { 30 | _tasks.push((async () => { 31 | const _current = path.join(path_, _file) 32 | const _stat = await fs.lstat(_current) 33 | const _extension = path.extname(_file) 34 | if (_stat.isFile() && _extension === '.js') { 35 | lib.log.trace(' >>> acquired js file', _current.substr(base.length + 1)) 36 | const _stricted = await lib.strict(_current) 37 | _stricted 38 | ? done.push(_current) 39 | : skipped.push(_current) 40 | } else if (_stat.isDirectory()) { 41 | if (ignore.find((ignore) => _current.endsWith(ignore))) { 42 | lib.log.trace(' --- ignore dir', _current.substr(path_.length + 1)) 43 | } else { 44 | const _report = await lib.run(_current, ignore, path_) 45 | done = done.concat(_report.done) 46 | skipped = skipped.concat(_report.skipped) 47 | } 48 | } 49 | })()) 50 | } 51 | await Promise.all(_tasks) 52 | return { done, skipped } 53 | }, 54 | 55 | /** 56 | * read first line in file lookng for 'use strict' declaration 57 | * 'use strict' will be added on top if missing 58 | * @param {string} file full path 59 | * @return {bool} true if added 'use strict', false if already has 60 | */ 61 | strict: async function (file) { 62 | let _handle 63 | try { 64 | _handle = await fs.open(file, 'r+') 65 | const _buffer = Buffer.alloc(BUFFER_SIZE) 66 | let _chunk 67 | let _line = '' 68 | let _offset = 0 69 | let bytesRead 70 | do { 71 | const read = await _handle.read(_buffer, 0, BUFFER_SIZE, _offset) 72 | bytesRead = read.bytesRead 73 | _chunk = _buffer.toString('utf8') 74 | _line += _chunk 75 | _offset += BUFFER_SIZE 76 | } while (!_chunk.includes('\n') && bytesRead > 0) 77 | 78 | if (_line.includes('use strict')) { 79 | return false 80 | } 81 | const _content = await _handle.readFile('utf8') 82 | await _handle.write("'use strict'\n\n" + _content, 0, 'utf8') 83 | return true 84 | } catch (error) { 85 | lib.log.error('ERROR', file, error) 86 | } finally { 87 | if (_handle) { 88 | await _handle.close() 89 | } 90 | } 91 | return false 92 | } 93 | } 94 | 95 | module.exports = lib 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "be-strict", 3 | "version": "1.2.1", 4 | "devDependencies": { 5 | "standard": "^16.0.4", 6 | "tap": "^15.0.10" 7 | }, 8 | "files": [ 9 | "bin/*", 10 | "lib/*" 11 | ], 12 | "scripts": { 13 | "test": "standard && tap test/basic.js", 14 | "test-coverage": "standard && tap test/basic.js --cov" 15 | }, 16 | "description": "add 'use strict' on the top of js files, if missing", 17 | "main": "main.js", 18 | "engines": { 19 | "node": ">= 14" 20 | }, 21 | "author": "Simone Sanfratello ", 22 | "license": "MIT", 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/simone-sanfratello/be-strict.git" 26 | }, 27 | "bin": { 28 | "be-strict": "bin/main.js" 29 | }, 30 | "keywords": [ 31 | "strict", 32 | "strict mode" 33 | ], 34 | "dependencies": { 35 | "yargs": "^17.2.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const { exec } = require('child_process') 5 | const tap = require('tap') 6 | 7 | const lib = require('../lib') 8 | 9 | const _repo = path.join(__dirname, './sample/repo') 10 | 11 | function execAsync (command) { 12 | return new Promise((resolve, reject) => { 13 | exec(command, (err, stdout, stderr) => { 14 | if (err) { 15 | reject(err) 16 | return 17 | } 18 | resolve() 19 | }) 20 | }) 21 | } 22 | 23 | tap.test('strict a repo', 24 | async (_test) => { 25 | _test.plan(1) 26 | await execAsync(`rm -rf ${_repo}; git clone https://github.com/braceslab/json-stringify-extended ${_repo}`) 27 | const _report = await lib.run(_repo) 28 | 29 | _test.ok(_report.skipped.length === 0 && _report.done.length === 3) 30 | }) 31 | 32 | tap.test('strict a repo again', 33 | async (_test) => { 34 | _test.plan(1) 35 | 36 | const _report = await lib.run(_repo) 37 | _test.ok(_report.skipped.length === 3 && _report.done.length === 0) 38 | }) 39 | --------------------------------------------------------------------------------