forked from microsoft/rushstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathScanAction.ts
More file actions
127 lines (103 loc) · 4.43 KB
/
ScanAction.ts
File metadata and controls
127 lines (103 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
/// <reference path="../../../typings/builtins/builtins.d.ts" />
import * as colors from 'colors';
import * as glob from 'glob';
import * as path from 'path';
import builtinPackageNames = require('builtins');
import { RushCommandLineParser } from '../RushCommandLineParser';
import { BaseConfiglessRushAction } from './BaseRushAction';
import { FileSystem } from '@microsoft/node-core-library';
export class ScanAction extends BaseConfiglessRushAction {
constructor(parser: RushCommandLineParser) {
super({
actionName: 'scan',
summary: 'Scan the current project folder and display a report of imported packages.',
documentation: `The NPM system allows a project to import dependencies without explicitly`
+ ` listing them in its package.json file. This is a dangerous practice, because`
+ ` there is no guarantee you will get a compatible version. The "rush scan" command`
+ ` reports a list of packages that are imported by your code, which you can`
+ ` compare against your package.json file to find mistakes. It searches the "./src"`
+ ` and "./lib" folders for typical import syntaxes such as "import __ from '__'",`
+ ` "require('__')", "System.import('__'), etc. The results are only approximate,`
+ ` but generally pretty accurate.`,
safeForSimultaneousRushProcesses: true,
parser
});
}
protected onDefineParameters(): void {
// abstract
}
protected run(): Promise<void> {
const packageJsonFilename: string = path.resolve('./package.json');
if (!FileSystem.exists(packageJsonFilename)) {
throw new Error('You must run "rush scan" in a project folder containing a package.json file.');
}
const requireRegExps: RegExp[] = [
// Example: require('someting')
/\brequire\s*\(\s*[']([^']+\s*)[']\)/,
/\brequire\s*\(\s*["]([^"]+)["]\s*\)/,
// Example: require.ensure('someting')
/\brequire.ensure\s*\(\s*[']([^']+\s*)[']\)/,
/\brequire.ensure\s*\(\s*["]([^"]+)["]\s*\)/,
// Example: require.resolve('someting')
/\brequire.resolve\s*\(\s*[']([^']+\s*)[']\)/,
/\brequire.resolve\s*\(\s*["]([^"]+)["]\s*\)/,
// Example: System.import('someting')
/\bSystem.import\s*\(\s*[']([^']+\s*)[']\)/,
/\bSystem.import\s*\(\s*["]([^"]+)["]\s*\)/,
// Example:
//
// import {
// A, B
// } from 'something';
/\bfrom\s*[']([^']+)[']/,
/\bfrom\s*["]([^"]+)["]/,
// Example: import 'something';
/\bimport\s*[']([^']+)[']\s*\;/,
/\bimport\s*["]([^"]+)["]\s*\;/,
// Example:
// /// <reference types="something" />
/\/\/\/\s*<\s*reference\s+types\s*=\s*["]([^"]+)["]\s*\/>/
];
// Example: "my-package/lad/dee/dah" --> "my-package"
// Example: "@ms/my-package" --> "@ms/my-package"
const packageRegExp: RegExp = /^((@[a-z\-0-9!_]+\/)?[a-z\-0-9!_]+)\/?/;
const requireMatches: Set<string> = new Set<string>();
for (const filename of glob.sync('{./*.{ts,js,tsx,jsx},./{src,lib}/**/*.{ts,js,tsx,jsx}}')) {
try {
const contents: string = FileSystem.readFile(filename);
const lines: string[] = contents.split('\n');
for (const line of lines) {
for (const requireRegExp of requireRegExps) {
const requireRegExpResult: RegExpExecArray | null = requireRegExp.exec(line);
if (requireRegExpResult) {
requireMatches.add(requireRegExpResult[1]);
}
}
}
} catch (error) {
console.log(colors.bold('Skipping file due to error: ' + filename));
}
}
const packageMatches: Set<string> = new Set<string>();
requireMatches.forEach((requireMatch: string) => {
const packageRegExpResult: RegExpExecArray | null = packageRegExp.exec(requireMatch);
if (packageRegExpResult) {
packageMatches.add(packageRegExpResult[1]);
}
});
const packageNames: string[] = [];
packageMatches.forEach((packageName: string) => {
packageNames.push(packageName);
});
packageNames.sort();
console.log('Detected dependencies:');
for (const packageName of packageNames) {
if (builtinPackageNames.indexOf(packageName) < 0) {
console.log(' ' + packageName);
}
}
return Promise.resolve();
}
}