Comparing version 0.1.3 to 0.1.4
@@ -7,4 +7,16 @@ "use strict"; | ||
commander.name('tyscan') | ||
.version('0.1.3', '-V, --version') | ||
.version('0.1.4', '-V, --version') | ||
.description('Command line tool for scanning TypeScript sources'); | ||
commander.command('init') | ||
.alias('i') | ||
.description('create sample tyscan.yml') | ||
.action((_) => { | ||
run(() => cli.init(), false); | ||
}); | ||
commander.command('console [path...]') | ||
.alias('c') | ||
.description('start interactive console') | ||
.action((paths, _) => { | ||
run(() => cli.console_(paths.length ? paths : ['.']), false); | ||
}); | ||
commander.command('scan [path...]') | ||
@@ -19,3 +31,6 @@ .alias('s') | ||
compiler_1.configureCompilerOptions(opts.tsconfig); | ||
run(() => cli.scan(paths.length ? paths : ['.'], opts.config, opts.json || false, opts.verbose || false, console.log, console.error)); | ||
const srcPaths = paths.length ? paths : ['.']; | ||
const jsonOutput = opts.json || false; | ||
const verbose = opts.verbose || false; | ||
run(() => cli.scan(srcPaths, opts.config, jsonOutput, verbose, console.log, console.error), jsonOutput); | ||
}); | ||
@@ -30,3 +45,4 @@ commander.command('test') | ||
compiler_1.configureCompilerOptions(opts.tsconfig); | ||
run(() => cli.test(opts.config, opts.json || false, console.log, console.error)); | ||
const jsonOutput = opts.json || false; | ||
run(() => cli.test(opts.config, jsonOutput, console.log, console.error), jsonOutput); | ||
}); | ||
@@ -38,3 +54,3 @@ if (process.argv.slice(2).length === 0) { | ||
commander.parse(process.argv); | ||
function run(f) { | ||
function run(f, jsonOutput) { | ||
let ecode = 1; | ||
@@ -45,3 +61,16 @@ try { | ||
catch (e) { | ||
console.error(`${e.stack}`); | ||
const err = e; | ||
if (jsonOutput) { | ||
const stacktrace = []; | ||
err.stack.split('\n') | ||
.slice(1) | ||
.map((s, i, a) => s.trim()) | ||
.map((s, i, a) => s.replace(/^at /, '')) | ||
.forEach((s, i, a) => stacktrace.push(s)); | ||
const json = { errors: [{ stacktrace, message: err.message }] }; | ||
console.log(JSON.stringify(json)); | ||
} | ||
else { | ||
console.error(`${err.stack}`); | ||
} | ||
} | ||
@@ -48,0 +77,0 @@ finally { |
129
dist/cli.js
@@ -6,23 +6,95 @@ "use strict"; | ||
const ts = require("typescript"); | ||
const os = require("os"); | ||
const promptSync = require("prompt-sync"); | ||
const promptSyncHistory = require("prompt-sync-history"); | ||
const config = require("./config"); | ||
const compiler = require("./compiler"); | ||
const patternParser = require("./pattern/parser"); | ||
const constants_1 = require("constants"); | ||
function init() { | ||
fs.copyFileSync(`${__dirname}/../sample/tyscan.yml`, 'tyscan.yml', constants_1.COPYFILE_EXCL); | ||
return 0; | ||
} | ||
exports.init = init; | ||
function console_(srcPaths) { | ||
console.log('TyScan console'); | ||
printConsoleHelp(); | ||
const paths = findTSFiles(srcPaths); | ||
const history = promptSyncHistory(`${os.homedir()}/.tyscan_history`); | ||
const prompt = promptSync({ history }); | ||
let program = compiler.compileFiles(paths); | ||
while (true) { | ||
let command = prompt('> '); | ||
if (command === null) { | ||
break; | ||
} | ||
command = command.trim(); | ||
if (command.length === 0) { | ||
continue; | ||
} | ||
if (command === 'exit') { | ||
break; | ||
} | ||
if (command === 'reload') { | ||
program = compiler.compileFiles(paths); | ||
continue; | ||
} | ||
if (!command.startsWith('find')) { | ||
console.log(`Unknown command: ${command}`); | ||
printConsoleHelp(); | ||
continue; | ||
} | ||
if (!command.match(/^find\s/)) { | ||
console.log('No pattern provided'); | ||
continue; | ||
} | ||
const patternString = command.substring(4).trim(); | ||
let pattern; | ||
try { | ||
pattern = patternParser.parse([patternString]); | ||
} | ||
catch (e) { | ||
console.log(`${e.stack}`); | ||
continue; | ||
} | ||
for (const path of filterNodeModules(paths)) { | ||
const result = compiler.createResult(program, path); | ||
if (result.isSuccessful()) { | ||
const nodes = pattern.scan(result.srcFile, result.program.getTypeChecker()); | ||
for (const node of nodes) { | ||
const start = ts.getLineAndCharacterOfPosition(result.srcFile, node.getStart()); | ||
const loc = `${path}#L${start.line + 1}C${start.character + 1}`; | ||
const txt = `${loc}\t${node.getText()}`; | ||
console.log(txt); | ||
} | ||
} | ||
else { | ||
const diags = result.program.getSyntacticDiagnostics(result.srcFile); | ||
for (const diag of diags) { | ||
const start = ts.getLineAndCharacterOfPosition(result.srcFile, diag.start); | ||
const msg = ts.flattenDiagnosticMessageText(diag.messageText, '\n'); | ||
console.log(`\x1b[31m${path}#L${start.line + 1}C${start.character + 1}: ${msg}\x1b[0m`); | ||
} | ||
} | ||
} | ||
} | ||
history.save(); | ||
return 0; | ||
} | ||
exports.console_ = console_; | ||
function printConsoleHelp() { | ||
console.log(); | ||
console.log('Available commands:'); | ||
console.log(' - find <pattern> Find <pattern>'); | ||
console.log(' - reload Reload TypeScript files'); | ||
console.log(' - exit Exit'); | ||
console.log(); | ||
} | ||
function scan(srcPaths, configPath, jsonOutput, verboseOutput, stdout, stderr) { | ||
const paths = srcPaths | ||
.filter(p => fs.existsSync(p)) | ||
.map(p => p.replace(/\/$/, '')) | ||
.map((p) => { | ||
if (fs.statSync(p).isDirectory()) { | ||
return fg.sync([ | ||
`${p}/**/*.ts`, | ||
`${p}/**/*.tsx`, | ||
'!**/node_modules/**', | ||
]).map(e => e.toString()); | ||
} | ||
return [p]; | ||
}) | ||
.reduce((acc, paths) => acc.concat(paths), []); | ||
const paths = findTSFiles(srcPaths); | ||
const output = { matches: [], errors: [] }; | ||
const ecode = 0; | ||
const targetFileCount = paths.length; | ||
const targetFileCount = filterNodeModules(paths).length; | ||
let scannedFileCount = 0; | ||
for (const result of config.load(configPath).scan(paths)) { | ||
for (const result of config.load(configPath).scan(filterNodeModules(paths))) { | ||
const src = result.compileResult.srcFile; | ||
@@ -40,3 +112,7 @@ scannedFileCount += 1; | ||
output.matches.push({ | ||
rule: { id: rule.id, message: rule.message }, | ||
rule: { | ||
id: rule.id, | ||
message: rule.message, | ||
justification: rule.justification === undefined ? null : rule.justification, | ||
}, | ||
path: result.path, | ||
@@ -72,2 +148,3 @@ location: { | ||
message: msg, | ||
path: diag.file.fileName, | ||
}); | ||
@@ -140,2 +217,20 @@ } | ||
exports.test = test; | ||
function findTSFiles(srcPaths) { | ||
return srcPaths | ||
.filter(p => fs.existsSync(p)) | ||
.map(p => p.replace(/\/$/, '')) | ||
.map((p) => { | ||
if (fs.statSync(p).isDirectory()) { | ||
return fg.sync([ | ||
`${p}/**/*.ts`, | ||
`${p}/**/*.tsx`, | ||
]).map(e => e.toString()); | ||
} | ||
return [p]; | ||
}) | ||
.reduce((acc, paths) => acc.concat(paths), []); | ||
} | ||
function filterNodeModules(paths) { | ||
return paths.filter((p, _) => !p.includes('node_modules/')); | ||
} | ||
//# sourceMappingURL=cli.js.map |
@@ -29,5 +29,6 @@ "use strict"; | ||
class Rule { | ||
constructor(id, message, pattern) { | ||
constructor(id, message, justification, pattern) { | ||
this.id = id; | ||
this.message = message; | ||
this.justification = justification; | ||
this.pattern = pattern; | ||
@@ -124,2 +125,6 @@ this.tests = []; | ||
} | ||
const justification = obj.justification; | ||
if (justification !== undefined && typeof justification !== 'string') { | ||
throw new Error(`"justification" must be a string (${id})`); | ||
} | ||
const tests = { match: undefined, unmatch: undefined }; | ||
@@ -134,3 +139,3 @@ if (obj.tests === null) { | ||
const pattern = loadPattern(obj.pattern, id); | ||
const rule = new Rule(id, message, pattern); | ||
const rule = new Rule(id, message, justification, pattern); | ||
rule.tests.push(...loadTestSuite(tests.match, true, rule)); | ||
@@ -137,0 +142,0 @@ rule.tests.push(...loadTestSuite(tests.unmatch, false, rule)); |
{ | ||
"name": "tyscan", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Command line tool for scanning TypeScript sources", | ||
@@ -25,2 +25,6 @@ "bin": { | ||
"dependencies": { | ||
"@types/prompt-sync": "^4.1.0", | ||
"@types/prompt-sync-history": "^1.0.1", | ||
"@types/readline-sync": "^1.4.3", | ||
"@types/tmp": "0.0.34", | ||
"commander": "^2.19.0", | ||
@@ -30,2 +34,6 @@ "fast-glob": "^2.2.6", | ||
"parsimmon": "^1.12.0", | ||
"prompt-sync": "^4.1.6", | ||
"prompt-sync-history": "^1.0.1", | ||
"readline-sync": "^1.4.9", | ||
"tmp": "0.0.33", | ||
"tsconfig": "^7.0.0" | ||
@@ -32,0 +40,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
87107
1040
14
+ Added@types/prompt-sync@^4.1.0
+ Added@types/readline-sync@^1.4.3
+ Added@types/tmp@0.0.34
+ Addedprompt-sync@^4.1.6
+ Addedprompt-sync-history@^1.0.1
+ Addedreadline-sync@^1.4.9
+ Addedtmp@0.0.33
+ Added@types/prompt-sync@4.2.3(transitive)
+ Added@types/prompt-sync-history@1.0.4(transitive)
+ Added@types/readline-sync@1.4.8(transitive)
+ Added@types/tmp@0.0.34(transitive)
+ Addedansi-regex@4.1.1(transitive)
+ Addedos-tmpdir@1.0.2(transitive)
+ Addedprompt-sync@4.2.0(transitive)
+ Addedprompt-sync-history@1.0.1(transitive)
+ Addedreadline-sync@1.4.10(transitive)
+ Addedstrip-ansi@5.2.0(transitive)
+ Addedtmp@0.0.33(transitive)