@rushstack/lockfile-explorer 1.4.16 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/bin/lockfile-explorer +1 -1
  2. package/bin/lockfile-lint +2 -0
  3. package/lib/assets/lint-init/lockfile-lint-template.json +42 -0
  4. package/lib/cli/explorer/ExplorerCommandLineParser.d.ts +12 -0
  5. package/lib/cli/explorer/ExplorerCommandLineParser.d.ts.map +1 -0
  6. package/lib/cli/explorer/ExplorerCommandLineParser.js +205 -0
  7. package/lib/cli/explorer/ExplorerCommandLineParser.js.map +1 -0
  8. package/lib/cli/lint/LintCommandLineParser.d.ts +10 -0
  9. package/lib/cli/lint/LintCommandLineParser.d.ts.map +1 -0
  10. package/lib/cli/lint/LintCommandLineParser.js +35 -0
  11. package/lib/cli/lint/LintCommandLineParser.js.map +1 -0
  12. package/lib/cli/lint/actions/CheckAction.d.ts +27 -0
  13. package/lib/cli/lint/actions/CheckAction.d.ts.map +1 -0
  14. package/lib/cli/lint/actions/CheckAction.js +145 -0
  15. package/lib/cli/lint/actions/CheckAction.js.map +1 -0
  16. package/lib/cli/lint/actions/InitAction.d.ts +8 -0
  17. package/lib/cli/lint/actions/InitAction.d.ts.map +1 -0
  18. package/lib/cli/lint/actions/InitAction.js +45 -0
  19. package/lib/cli/lint/actions/InitAction.js.map +1 -0
  20. package/lib/constants/common.d.ts +3 -0
  21. package/lib/constants/common.d.ts.map +1 -0
  22. package/lib/constants/common.js +8 -0
  23. package/lib/constants/common.js.map +1 -0
  24. package/lib/schemas/lockfile-lint.schema.json +45 -0
  25. package/lib/start-explorer.d.ts +2 -0
  26. package/lib/start-explorer.d.ts.map +1 -0
  27. package/lib/start-explorer.js +8 -0
  28. package/lib/start-explorer.js.map +1 -0
  29. package/lib/start-lint.d.ts +2 -0
  30. package/lib/start-lint.d.ts.map +1 -0
  31. package/lib/start-lint.js +8 -0
  32. package/lib/start-lint.js.map +1 -0
  33. package/lib/{init.d.ts → utils/init.d.ts} +1 -1
  34. package/lib/utils/init.d.ts.map +1 -0
  35. package/lib/{init.js → utils/init.js} +1 -1
  36. package/lib/utils/init.js.map +1 -0
  37. package/lib/utils/shrinkwrap.d.ts +11 -0
  38. package/lib/utils/shrinkwrap.d.ts.map +1 -0
  39. package/lib/{utils.js → utils/shrinkwrap.js} +37 -2
  40. package/lib/utils/shrinkwrap.js.map +1 -0
  41. package/package.json +14 -8
  42. package/lib/commandLine.d.ts +0 -7
  43. package/lib/commandLine.d.ts.map +0 -1
  44. package/lib/commandLine.js +0 -53
  45. package/lib/commandLine.js.map +0 -1
  46. package/lib/commandLine.test.d.ts +0 -2
  47. package/lib/commandLine.test.d.ts.map +0 -1
  48. package/lib/commandLine.test.js.map +0 -1
  49. package/lib/init.d.ts.map +0 -1
  50. package/lib/init.js.map +0 -1
  51. package/lib/start.d.ts +0 -2
  52. package/lib/start.d.ts.map +0 -1
  53. package/lib/start.js +0 -215
  54. package/lib/start.js.map +0 -1
  55. package/lib/utils.d.ts +0 -2
  56. package/lib/utils.d.ts.map +0 -1
  57. package/lib/utils.js.map +0 -1
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- require('../lib/start.js');
2
+ require('../lib/start-explorer.js');
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../lib/start-lint.js');
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Config file for Lockfile Lint. For more info, please visit: https://lfx.rushstack.io
3
+ */
4
+ {
5
+ "$schema": "https://developer.microsoft.com/json-schemas/lockfile-explorer/lockfile-lint.schema.json",
6
+
7
+ /**
8
+ * The list of rules to be checked by Lockfile Lint. For each rule configuration, the
9
+ * type of rule is determined by the `rule` field.
10
+ */
11
+ "rules": [
12
+ // /**
13
+ // * The `restrict-versions` rule enforces that direct and indirect dependencies must
14
+ // * satisfy a specified version range.
15
+ // */
16
+ // {
17
+ // "rule": "restrict-versions",
18
+ //
19
+ // /**
20
+ // * The name of a workspace project to analyze.
21
+ // */
22
+ // "project": "@my-company/my-app",
23
+ //
24
+ // /**
25
+ // * Indicates the package versions to be checked. The `requiredVersions` key is
26
+ // * the name of an NPM package, and the value is a SemVer range. If the project has
27
+ // * that NPM package as a dependency, then its version must satisfy the SemVer range.
28
+ // * This check also applies to devDependencies and peerDependencies, as well as any
29
+ // * indirect dependencies of the project.
30
+ // */
31
+ // "requiredVersions": {
32
+ // /**
33
+ // * For example, if `react-router` appears anywhere in the dependency graph of
34
+ // * `@my-company/my-app`, then it must be version 5 or 6.
35
+ // */
36
+ // "react-router": "5.x || 6.x",
37
+ // "react": "^18.3.0",
38
+ // "react-dom": "^18.3.0"
39
+ // }
40
+ // }
41
+ ]
42
+ }
@@ -0,0 +1,12 @@
1
+ import { type ITerminal } from '@rushstack/terminal';
2
+ import { CommandLineParser } from '@rushstack/ts-command-line';
3
+ export declare class ExplorerCommandLineParser extends CommandLineParser {
4
+ readonly globalTerminal: ITerminal;
5
+ private readonly _terminalProvider;
6
+ private readonly _debugParameter;
7
+ private readonly _subspaceParameter;
8
+ constructor();
9
+ get isDebug(): boolean;
10
+ protected onExecute(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=ExplorerCommandLineParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExplorerCommandLineParser.d.ts","sourceRoot":"","sources":["../../../src/cli/explorer/ExplorerCommandLineParser.ts"],"names":[],"mappings":"AAWA,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,qBAAqB,CAAC;AAClG,OAAO,EAEL,iBAAiB,EAElB,MAAM,4BAA4B,CAAC;AAUpC,qBAAa,yBAA0B,SAAQ,iBAAiB;IAC9D,SAAgB,cAAc,EAAE,SAAS,CAAC;IAC1C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0B;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAE3D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAsC;;IA0BzE,IAAW,OAAO,IAAI,OAAO,CAE5B;cAEe,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CA2L3C"}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ var __importDefault = (this && this.__importDefault) || function (mod) {
5
+ return (mod && mod.__esModule) ? mod : { "default": mod };
6
+ };
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ExplorerCommandLineParser = void 0;
9
+ const express_1 = __importDefault(require("express"));
10
+ const js_yaml_1 = __importDefault(require("js-yaml"));
11
+ const cors_1 = __importDefault(require("cors"));
12
+ const process_1 = __importDefault(require("process"));
13
+ const open_1 = __importDefault(require("open"));
14
+ const update_notifier_1 = __importDefault(require("update-notifier"));
15
+ const node_core_library_1 = require("@rushstack/node-core-library");
16
+ const terminal_1 = require("@rushstack/terminal");
17
+ const ts_command_line_1 = require("@rushstack/ts-command-line");
18
+ const init_1 = require("../../utils/init");
19
+ const shrinkwrap_1 = require("../../utils/shrinkwrap");
20
+ const EXPLORER_TOOL_FILENAME = 'lockfile-explorer';
21
+ class ExplorerCommandLineParser extends ts_command_line_1.CommandLineParser {
22
+ constructor() {
23
+ super({
24
+ toolFilename: EXPLORER_TOOL_FILENAME,
25
+ toolDescription: 'Lockfile Explorer is a desktop app for investigating and solving version conflicts in a PNPM workspace.'
26
+ });
27
+ this._debugParameter = this.defineFlagParameter({
28
+ parameterLongName: '--debug',
29
+ parameterShortName: '-d',
30
+ description: 'Show the full call stack if an error occurs while executing the tool'
31
+ });
32
+ this._subspaceParameter = this.defineStringParameter({
33
+ parameterLongName: '--subspace',
34
+ argumentName: 'SUBSPACE_NAME',
35
+ description: 'Specifies an individual Rush subspace to check.',
36
+ defaultValue: 'default'
37
+ });
38
+ this._terminalProvider = new terminal_1.ConsoleTerminalProvider();
39
+ this.globalTerminal = new terminal_1.Terminal(this._terminalProvider);
40
+ }
41
+ get isDebug() {
42
+ return this._debugParameter.value;
43
+ }
44
+ async onExecute() {
45
+ const lockfileExplorerProjectRoot = node_core_library_1.PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname);
46
+ const lockfileExplorerPackageJson = node_core_library_1.JsonFile.load(`${lockfileExplorerProjectRoot}/package.json`);
47
+ const appVersion = lockfileExplorerPackageJson.version;
48
+ this.globalTerminal.writeLine(terminal_1.Colorize.bold(`\nRush Lockfile Explorer ${appVersion}`) +
49
+ terminal_1.Colorize.cyan(' - https://lfx.rushstack.io/\n'));
50
+ (0, update_notifier_1.default)({
51
+ pkg: lockfileExplorerPackageJson,
52
+ // Normally update-notifier waits a day or so before it starts displaying upgrade notices.
53
+ // In debug mode, show the notice right away.
54
+ updateCheckInterval: this.isDebug ? 0 : undefined
55
+ }).notify({
56
+ // Make sure it says "-g" in the "npm install" example command line
57
+ isGlobal: true,
58
+ // Show the notice immediately, rather than waiting for process.onExit()
59
+ defer: false
60
+ });
61
+ const PORT = 8091;
62
+ // Must not have a trailing slash
63
+ const SERVICE_URL = `http://localhost:${PORT}`;
64
+ const appState = (0, init_1.init)({
65
+ lockfileExplorerProjectRoot,
66
+ appVersion,
67
+ debugMode: this.isDebug,
68
+ subspaceName: this._subspaceParameter.value
69
+ });
70
+ // Important: This must happen after init() reads the current working directory
71
+ process_1.default.chdir(appState.lockfileExplorerProjectRoot);
72
+ const distFolderPath = `${appState.lockfileExplorerProjectRoot}/dist`;
73
+ const app = (0, express_1.default)();
74
+ app.use(express_1.default.json());
75
+ app.use((0, cors_1.default)());
76
+ // Variable used to check if the front-end client is still connected
77
+ let awaitingFirstConnect = true;
78
+ let isClientConnected = false;
79
+ let disconnected = false;
80
+ setInterval(() => {
81
+ if (!isClientConnected && !awaitingFirstConnect && !disconnected) {
82
+ console.log(terminal_1.Colorize.red('The client has disconnected!'));
83
+ console.log(`Please open a browser window at http://localhost:${PORT}/app`);
84
+ disconnected = true;
85
+ }
86
+ else if (!awaitingFirstConnect) {
87
+ isClientConnected = false;
88
+ }
89
+ }, 4000);
90
+ // This takes precedence over the `/app` static route, which also has an `initappcontext.js` file.
91
+ app.get('/initappcontext.js', (req, res) => {
92
+ const appContext = {
93
+ serviceUrl: SERVICE_URL,
94
+ appVersion: appState.appVersion,
95
+ debugMode: this.isDebug
96
+ };
97
+ const sourceCode = [
98
+ `console.log('Loaded initappcontext.js');`,
99
+ `appContext = ${JSON.stringify(appContext)}`
100
+ ].join('\n');
101
+ res.type('application/javascript').send(sourceCode);
102
+ });
103
+ app.use('/', express_1.default.static(distFolderPath));
104
+ app.use('/favicon.ico', express_1.default.static(distFolderPath, { index: 'favicon.ico' }));
105
+ app.get('/api/lockfile', async (req, res) => {
106
+ const pnpmLockfileText = await node_core_library_1.FileSystem.readFileAsync(appState.pnpmLockfileLocation);
107
+ const doc = js_yaml_1.default.load(pnpmLockfileText);
108
+ const { packages, lockfileVersion } = doc;
109
+ const shrinkwrapFileMajorVersion = (0, shrinkwrap_1.getShrinkwrapFileMajorVersion)(lockfileVersion);
110
+ if (packages && shrinkwrapFileMajorVersion === 6) {
111
+ const updatedPackages = {};
112
+ for (const [dependencyPath, dependency] of Object.entries(packages)) {
113
+ updatedPackages[(0, shrinkwrap_1.convertLockfileV6DepPathToV5DepPath)(dependencyPath)] = dependency;
114
+ }
115
+ doc.packages = updatedPackages;
116
+ }
117
+ res.send({
118
+ doc,
119
+ subspaceName: this._subspaceParameter.value
120
+ });
121
+ });
122
+ app.get('/api/health', (req, res) => {
123
+ awaitingFirstConnect = false;
124
+ isClientConnected = true;
125
+ if (disconnected) {
126
+ disconnected = false;
127
+ console.log(terminal_1.Colorize.green('The client has reconnected!'));
128
+ }
129
+ res.status(200).send();
130
+ });
131
+ app.post('/api/package-json', async (req, res) => {
132
+ const { projectPath } = req.body;
133
+ const fileLocation = `${appState.projectRoot}/${projectPath}/package.json`;
134
+ let packageJsonText;
135
+ try {
136
+ packageJsonText = await node_core_library_1.FileSystem.readFileAsync(fileLocation);
137
+ }
138
+ catch (e) {
139
+ if (node_core_library_1.FileSystem.isNotExistError(e)) {
140
+ return res.status(404).send({
141
+ message: `Could not load package.json file for this package. Have you installed all the dependencies for this workspace?`,
142
+ error: `No package.json in location: ${projectPath}`
143
+ });
144
+ }
145
+ else {
146
+ throw e;
147
+ }
148
+ }
149
+ res.send(packageJsonText);
150
+ });
151
+ app.get('/api/pnpmfile', async (req, res) => {
152
+ let pnpmfile;
153
+ try {
154
+ pnpmfile = await node_core_library_1.FileSystem.readFileAsync(appState.pnpmfileLocation);
155
+ }
156
+ catch (e) {
157
+ if (node_core_library_1.FileSystem.isNotExistError(e)) {
158
+ return res.status(404).send({
159
+ message: `Could not load pnpmfile file in this repo.`,
160
+ error: `No .pnpmifile.cjs found.`
161
+ });
162
+ }
163
+ else {
164
+ throw e;
165
+ }
166
+ }
167
+ res.send(pnpmfile);
168
+ });
169
+ app.post('/api/package-spec', async (req, res) => {
170
+ const { projectPath } = req.body;
171
+ const fileLocation = `${appState.projectRoot}/${projectPath}/package.json`;
172
+ let packageJson;
173
+ try {
174
+ packageJson = await node_core_library_1.JsonFile.loadAsync(fileLocation);
175
+ }
176
+ catch (e) {
177
+ if (node_core_library_1.FileSystem.isNotExistError(e)) {
178
+ return res.status(404).send({
179
+ message: `Could not load package.json file in location: ${projectPath}`
180
+ });
181
+ }
182
+ else {
183
+ throw e;
184
+ }
185
+ }
186
+ const { hooks: { readPackage } } = require(appState.pnpmfileLocation);
187
+ const parsedPackage = readPackage(packageJson, {});
188
+ res.send(parsedPackage);
189
+ });
190
+ app.listen(PORT, async () => {
191
+ console.log(`App launched on ${SERVICE_URL}`);
192
+ if (!appState.debugMode) {
193
+ try {
194
+ // Launch the web browser
195
+ await (0, open_1.default)(SERVICE_URL);
196
+ }
197
+ catch (e) {
198
+ this.globalTerminal.writeError('Error launching browser: ' + e.toString());
199
+ }
200
+ }
201
+ });
202
+ }
203
+ }
204
+ exports.ExplorerCommandLineParser = ExplorerCommandLineParser;
205
+ //# sourceMappingURL=ExplorerCommandLineParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExplorerCommandLineParser.js","sourceRoot":"","sources":["../../../src/cli/explorer/ExplorerCommandLineParser.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport express from 'express';\nimport yaml from 'js-yaml';\nimport cors from 'cors';\nimport process from 'process';\nimport open from 'open';\nimport updateNotifier from 'update-notifier';\n\nimport { FileSystem, type IPackageJson, JsonFile, PackageJsonLookup } from '@rushstack/node-core-library';\nimport { ConsoleTerminalProvider, type ITerminal, Terminal, Colorize } from '@rushstack/terminal';\nimport {\n type CommandLineFlagParameter,\n CommandLineParser,\n type IRequiredCommandLineStringParameter\n} from '@rushstack/ts-command-line';\nimport type { IAppContext } from '@rushstack/lockfile-explorer-web/lib/AppContext';\nimport type { Lockfile } from '@pnpm/lockfile-types';\n\nimport type { IAppState } from '../../state';\nimport { init } from '../../utils/init';\nimport { convertLockfileV6DepPathToV5DepPath, getShrinkwrapFileMajorVersion } from '../../utils/shrinkwrap';\n\nconst EXPLORER_TOOL_FILENAME: 'lockfile-explorer' = 'lockfile-explorer';\n\nexport class ExplorerCommandLineParser extends CommandLineParser {\n public readonly globalTerminal: ITerminal;\n private readonly _terminalProvider: ConsoleTerminalProvider;\n private readonly _debugParameter: CommandLineFlagParameter;\n\n private readonly _subspaceParameter: IRequiredCommandLineStringParameter;\n\n public constructor() {\n super({\n toolFilename: EXPLORER_TOOL_FILENAME,\n toolDescription:\n 'Lockfile Explorer is a desktop app for investigating and solving version conflicts in a PNPM workspace.'\n });\n\n this._debugParameter = this.defineFlagParameter({\n parameterLongName: '--debug',\n parameterShortName: '-d',\n description: 'Show the full call stack if an error occurs while executing the tool'\n });\n\n this._subspaceParameter = this.defineStringParameter({\n parameterLongName: '--subspace',\n argumentName: 'SUBSPACE_NAME',\n description: 'Specifies an individual Rush subspace to check.',\n defaultValue: 'default'\n });\n\n this._terminalProvider = new ConsoleTerminalProvider();\n this.globalTerminal = new Terminal(this._terminalProvider);\n }\n\n public get isDebug(): boolean {\n return this._debugParameter.value;\n }\n\n protected async onExecute(): Promise<void> {\n const lockfileExplorerProjectRoot: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!;\n const lockfileExplorerPackageJson: IPackageJson = JsonFile.load(\n `${lockfileExplorerProjectRoot}/package.json`\n );\n const appVersion: string = lockfileExplorerPackageJson.version;\n\n this.globalTerminal.writeLine(\n Colorize.bold(`\\nRush Lockfile Explorer ${appVersion}`) +\n Colorize.cyan(' - https://lfx.rushstack.io/\\n')\n );\n\n updateNotifier({\n pkg: lockfileExplorerPackageJson,\n // Normally update-notifier waits a day or so before it starts displaying upgrade notices.\n // In debug mode, show the notice right away.\n updateCheckInterval: this.isDebug ? 0 : undefined\n }).notify({\n // Make sure it says \"-g\" in the \"npm install\" example command line\n isGlobal: true,\n // Show the notice immediately, rather than waiting for process.onExit()\n defer: false\n });\n\n const PORT: number = 8091;\n // Must not have a trailing slash\n const SERVICE_URL: string = `http://localhost:${PORT}`;\n\n const appState: IAppState = init({\n lockfileExplorerProjectRoot,\n appVersion,\n debugMode: this.isDebug,\n subspaceName: this._subspaceParameter.value\n });\n\n // Important: This must happen after init() reads the current working directory\n process.chdir(appState.lockfileExplorerProjectRoot);\n\n const distFolderPath: string = `${appState.lockfileExplorerProjectRoot}/dist`;\n const app: express.Application = express();\n app.use(express.json());\n app.use(cors());\n\n // Variable used to check if the front-end client is still connected\n let awaitingFirstConnect: boolean = true;\n let isClientConnected: boolean = false;\n let disconnected: boolean = false;\n setInterval(() => {\n if (!isClientConnected && !awaitingFirstConnect && !disconnected) {\n console.log(Colorize.red('The client has disconnected!'));\n console.log(`Please open a browser window at http://localhost:${PORT}/app`);\n disconnected = true;\n } else if (!awaitingFirstConnect) {\n isClientConnected = false;\n }\n }, 4000);\n\n // This takes precedence over the `/app` static route, which also has an `initappcontext.js` file.\n app.get('/initappcontext.js', (req: express.Request, res: express.Response) => {\n const appContext: IAppContext = {\n serviceUrl: SERVICE_URL,\n appVersion: appState.appVersion,\n debugMode: this.isDebug\n };\n const sourceCode: string = [\n `console.log('Loaded initappcontext.js');`,\n `appContext = ${JSON.stringify(appContext)}`\n ].join('\\n');\n\n res.type('application/javascript').send(sourceCode);\n });\n\n app.use('/', express.static(distFolderPath));\n\n app.use('/favicon.ico', express.static(distFolderPath, { index: 'favicon.ico' }));\n\n app.get('/api/lockfile', async (req: express.Request, res: express.Response) => {\n const pnpmLockfileText: string = await FileSystem.readFileAsync(appState.pnpmLockfileLocation);\n const doc = yaml.load(pnpmLockfileText) as Lockfile;\n const { packages, lockfileVersion } = doc;\n\n const shrinkwrapFileMajorVersion: number = getShrinkwrapFileMajorVersion(lockfileVersion);\n\n if (packages && shrinkwrapFileMajorVersion === 6) {\n const updatedPackages: Lockfile['packages'] = {};\n for (const [dependencyPath, dependency] of Object.entries(packages)) {\n updatedPackages[convertLockfileV6DepPathToV5DepPath(dependencyPath)] = dependency;\n }\n doc.packages = updatedPackages;\n }\n\n res.send({\n doc,\n subspaceName: this._subspaceParameter.value\n });\n });\n\n app.get('/api/health', (req: express.Request, res: express.Response) => {\n awaitingFirstConnect = false;\n isClientConnected = true;\n if (disconnected) {\n disconnected = false;\n console.log(Colorize.green('The client has reconnected!'));\n }\n res.status(200).send();\n });\n\n app.post(\n '/api/package-json',\n async (req: express.Request<{}, {}, { projectPath: string }, {}>, res: express.Response) => {\n const { projectPath } = req.body;\n const fileLocation = `${appState.projectRoot}/${projectPath}/package.json`;\n let packageJsonText: string;\n try {\n packageJsonText = await FileSystem.readFileAsync(fileLocation);\n } catch (e) {\n if (FileSystem.isNotExistError(e)) {\n return res.status(404).send({\n message: `Could not load package.json file for this package. Have you installed all the dependencies for this workspace?`,\n error: `No package.json in location: ${projectPath}`\n });\n } else {\n throw e;\n }\n }\n\n res.send(packageJsonText);\n }\n );\n\n app.get('/api/pnpmfile', async (req: express.Request, res: express.Response) => {\n let pnpmfile: string;\n try {\n pnpmfile = await FileSystem.readFileAsync(appState.pnpmfileLocation);\n } catch (e) {\n if (FileSystem.isNotExistError(e)) {\n return res.status(404).send({\n message: `Could not load pnpmfile file in this repo.`,\n error: `No .pnpmifile.cjs found.`\n });\n } else {\n throw e;\n }\n }\n\n res.send(pnpmfile);\n });\n\n app.post(\n '/api/package-spec',\n async (req: express.Request<{}, {}, { projectPath: string }, {}>, res: express.Response) => {\n const { projectPath } = req.body;\n const fileLocation = `${appState.projectRoot}/${projectPath}/package.json`;\n let packageJson: IPackageJson;\n try {\n packageJson = await JsonFile.loadAsync(fileLocation);\n } catch (e) {\n if (FileSystem.isNotExistError(e)) {\n return res.status(404).send({\n message: `Could not load package.json file in location: ${projectPath}`\n });\n } else {\n throw e;\n }\n }\n\n const {\n hooks: { readPackage }\n } = require(appState.pnpmfileLocation);\n const parsedPackage = readPackage(packageJson, {});\n res.send(parsedPackage);\n }\n );\n\n app.listen(PORT, async () => {\n console.log(`App launched on ${SERVICE_URL}`);\n\n if (!appState.debugMode) {\n try {\n // Launch the web browser\n await open(SERVICE_URL);\n } catch (e) {\n this.globalTerminal.writeError('Error launching browser: ' + e.toString());\n }\n }\n });\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { type ITerminal } from '@rushstack/terminal';
2
+ import { CommandLineParser } from '@rushstack/ts-command-line';
3
+ export declare class LintCommandLineParser extends CommandLineParser {
4
+ readonly globalTerminal: ITerminal;
5
+ private readonly _terminalProvider;
6
+ constructor();
7
+ protected onExecute(): Promise<void>;
8
+ private _populateActions;
9
+ }
10
+ //# sourceMappingURL=LintCommandLineParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LintCommandLineParser.d.ts","sourceRoot":"","sources":["../../../src/cli/lint/LintCommandLineParser.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,qBAAqB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAO/D,qBAAa,qBAAsB,SAAQ,iBAAiB;IAC1D,SAAgB,cAAc,EAAE,SAAS,CAAC;IAC1C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0B;;cAenC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAcnD,OAAO,CAAC,gBAAgB;CAIzB"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.LintCommandLineParser = void 0;
6
+ const terminal_1 = require("@rushstack/terminal");
7
+ const ts_command_line_1 = require("@rushstack/ts-command-line");
8
+ const InitAction_1 = require("./actions/InitAction");
9
+ const CheckAction_1 = require("./actions/CheckAction");
10
+ const node_core_library_1 = require("@rushstack/node-core-library");
11
+ const LINT_TOOL_FILENAME = 'lockfile-lint';
12
+ class LintCommandLineParser extends ts_command_line_1.CommandLineParser {
13
+ constructor() {
14
+ super({
15
+ toolFilename: LINT_TOOL_FILENAME,
16
+ toolDescription: 'Lockfile Lint applies configured policies to find and report dependency issues in your PNPM workspace.'
17
+ });
18
+ this._terminalProvider = new terminal_1.ConsoleTerminalProvider();
19
+ this.globalTerminal = new terminal_1.Terminal(this._terminalProvider);
20
+ this._populateActions();
21
+ }
22
+ async onExecute() {
23
+ const lockfileExplorerProjectRoot = node_core_library_1.PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname);
24
+ const lockfileExplorerPackageJson = node_core_library_1.JsonFile.load(`${lockfileExplorerProjectRoot}/package.json`);
25
+ const appVersion = lockfileExplorerPackageJson.version;
26
+ this.globalTerminal.writeLine(terminal_1.Colorize.bold(`\nRush Lockfile Lint ${appVersion}`) + terminal_1.Colorize.cyan(' - https://lfx.rushstack.io/\n'));
27
+ await super.onExecute();
28
+ }
29
+ _populateActions() {
30
+ this.addAction(new InitAction_1.InitAction(this));
31
+ this.addAction(new CheckAction_1.CheckAction(this));
32
+ }
33
+ }
34
+ exports.LintCommandLineParser = LintCommandLineParser;
35
+ //# sourceMappingURL=LintCommandLineParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LintCommandLineParser.js","sourceRoot":"","sources":["../../../src/cli/lint/LintCommandLineParser.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,kDAAkG;AAClG,gEAA+D;AAC/D,qDAAkD;AAClD,uDAAoD;AACpD,oEAA8F;AAE9F,MAAM,kBAAkB,GAAoB,eAAe,CAAC;AAE5D,MAAa,qBAAsB,SAAQ,mCAAiB;IAI1D;QACE,KAAK,CAAC;YACJ,YAAY,EAAE,kBAAkB;YAChC,eAAe,EACb,wGAAwG;SAC3G,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,GAAG,IAAI,kCAAuB,EAAE,CAAC;QACvD,IAAI,CAAC,cAAc,GAAG,IAAI,mBAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAE3D,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEkB,KAAK,CAAC,SAAS;QAChC,MAAM,2BAA2B,GAAW,qCAAiB,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAE,CAAC;QAC1G,MAAM,2BAA2B,GAAiB,4BAAQ,CAAC,IAAI,CAC7D,GAAG,2BAA2B,eAAe,CAC9C,CAAC;QACF,MAAM,UAAU,GAAW,2BAA2B,CAAC,OAAO,CAAC;QAE/D,IAAI,CAAC,cAAc,CAAC,SAAS,CAC3B,mBAAQ,CAAC,IAAI,CAAC,wBAAwB,UAAU,EAAE,CAAC,GAAG,mBAAQ,CAAC,IAAI,CAAC,gCAAgC,CAAC,CACtG,CAAC;QAEF,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;IAC1B,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,SAAS,CAAC,IAAI,uBAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,SAAS,CAAC,IAAI,yBAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;CACF;AAnCD,sDAmCC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { ConsoleTerminalProvider, type ITerminal, Terminal, Colorize } from '@rushstack/terminal';\nimport { CommandLineParser } from '@rushstack/ts-command-line';\nimport { InitAction } from './actions/InitAction';\nimport { CheckAction } from './actions/CheckAction';\nimport { type IPackageJson, JsonFile, PackageJsonLookup } from '@rushstack/node-core-library';\n\nconst LINT_TOOL_FILENAME: 'lockfile-lint' = 'lockfile-lint';\n\nexport class LintCommandLineParser extends CommandLineParser {\n public readonly globalTerminal: ITerminal;\n private readonly _terminalProvider: ConsoleTerminalProvider;\n\n public constructor() {\n super({\n toolFilename: LINT_TOOL_FILENAME,\n toolDescription:\n 'Lockfile Lint applies configured policies to find and report dependency issues in your PNPM workspace.'\n });\n\n this._terminalProvider = new ConsoleTerminalProvider();\n this.globalTerminal = new Terminal(this._terminalProvider);\n\n this._populateActions();\n }\n\n protected override async onExecute(): Promise<void> {\n const lockfileExplorerProjectRoot: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!;\n const lockfileExplorerPackageJson: IPackageJson = JsonFile.load(\n `${lockfileExplorerProjectRoot}/package.json`\n );\n const appVersion: string = lockfileExplorerPackageJson.version;\n\n this.globalTerminal.writeLine(\n Colorize.bold(`\\nRush Lockfile Lint ${appVersion}`) + Colorize.cyan(' - https://lfx.rushstack.io/\\n')\n );\n\n await super.onExecute();\n }\n\n private _populateActions(): void {\n this.addAction(new InitAction(this));\n this.addAction(new CheckAction(this));\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { CommandLineAction } from '@rushstack/ts-command-line';
2
+ import type { LintCommandLineParser } from '../LintCommandLineParser';
3
+ export interface ILintRule {
4
+ rule: 'restrict-versions';
5
+ project: string;
6
+ requiredVersions: Record<string, string>;
7
+ }
8
+ export interface ILockfileLint {
9
+ rules: ILintRule[];
10
+ }
11
+ export interface ILintIssue {
12
+ project: string;
13
+ rule: string;
14
+ message: string;
15
+ }
16
+ export declare class CheckAction extends CommandLineAction {
17
+ private readonly _terminal;
18
+ private _rushConfiguration;
19
+ private _checkedProjects;
20
+ private _docMap;
21
+ constructor(parser: LintCommandLineParser);
22
+ private _checkVersionCompatibilityAsync;
23
+ private _searchAndValidateDependenciesAsync;
24
+ private _performVersionRestrictionCheckAsync;
25
+ protected onExecute(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=CheckAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckAction.d.ts","sourceRoot":"","sources":["../../../../src/cli/lint/actions/CheckAction.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAS/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAQtE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,WAAY,SAAQ,iBAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,OAAO,CAAqC;gBAEjC,MAAM,EAAE,qBAAqB;YAelC,+BAA+B;YAqC/B,mCAAmC;YA4DnC,oCAAoC;cAoBlC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAkE3C"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ var __importDefault = (this && this.__importDefault) || function (mod) {
5
+ return (mod && mod.__esModule) ? mod : { "default": mod };
6
+ };
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.CheckAction = void 0;
9
+ const terminal_1 = require("@rushstack/terminal");
10
+ const ts_command_line_1 = require("@rushstack/ts-command-line");
11
+ const rush_sdk_1 = require("@rushstack/rush-sdk");
12
+ const path_1 = __importDefault(require("path"));
13
+ const js_yaml_1 = __importDefault(require("js-yaml"));
14
+ const semver_1 = __importDefault(require("semver"));
15
+ const node_core_library_1 = require("@rushstack/node-core-library");
16
+ const lockfile_lint_schema_json_1 = __importDefault(require("../../../schemas/lockfile-lint.schema.json"));
17
+ const common_1 = require("../../../constants/common");
18
+ const shrinkwrap_1 = require("../../../utils/shrinkwrap");
19
+ class CheckAction extends ts_command_line_1.CommandLineAction {
20
+ constructor(parser) {
21
+ super({
22
+ actionName: 'check',
23
+ summary: 'Check and report dependency issues in your workspace',
24
+ documentation: 'This command applies the policies that are configured in ' +
25
+ common_1.LOCKFILE_LINT_JSON_FILENAME +
26
+ ', reporting any problems found in your PNPM workspace.'
27
+ });
28
+ this._terminal = parser.globalTerminal;
29
+ this._checkedProjects = new Set();
30
+ this._docMap = new Map();
31
+ }
32
+ async _checkVersionCompatibilityAsync(shrinkwrapFileMajorVersion, packages, dependencyPath, requiredVersions, checkedDependencyPaths) {
33
+ var _a;
34
+ if (packages && packages[dependencyPath] && !checkedDependencyPaths.has(dependencyPath)) {
35
+ checkedDependencyPaths.add(dependencyPath);
36
+ const { name, version } = (0, shrinkwrap_1.parseDependencyPath)(shrinkwrapFileMajorVersion, dependencyPath);
37
+ if (name in requiredVersions && !semver_1.default.satisfies(version, requiredVersions[name])) {
38
+ throw new Error(`The version of "${name}" should match "${requiredVersions[name]}";` +
39
+ ` actual version is "${version}"`);
40
+ }
41
+ await Promise.all(Object.entries((_a = packages[dependencyPath].dependencies) !== null && _a !== void 0 ? _a : {}).map(async ([dependencyPackageName, dependencyPackageVersion]) => {
42
+ await this._checkVersionCompatibilityAsync(shrinkwrapFileMajorVersion, packages, (0, shrinkwrap_1.splicePackageWithVersion)(shrinkwrapFileMajorVersion, dependencyPackageName, dependencyPackageVersion), requiredVersions, checkedDependencyPaths);
43
+ }));
44
+ }
45
+ }
46
+ async _searchAndValidateDependenciesAsync(project, requiredVersions) {
47
+ this._terminal.writeLine(`Checking project "${project.packageName}"`);
48
+ const projectFolder = project.projectFolder;
49
+ const subspace = project.subspace;
50
+ const shrinkwrapFilename = subspace.getCommittedShrinkwrapFilename();
51
+ let doc;
52
+ if (this._docMap.has(shrinkwrapFilename)) {
53
+ doc = this._docMap.get(shrinkwrapFilename);
54
+ }
55
+ else {
56
+ const pnpmLockfileText = await node_core_library_1.FileSystem.readFileAsync(shrinkwrapFilename);
57
+ doc = js_yaml_1.default.load(pnpmLockfileText);
58
+ this._docMap.set(shrinkwrapFilename, doc);
59
+ }
60
+ const { importers, lockfileVersion, packages } = doc;
61
+ const shrinkwrapFileMajorVersion = (0, shrinkwrap_1.getShrinkwrapFileMajorVersion)(lockfileVersion);
62
+ const checkedDependencyPaths = new Set();
63
+ await Promise.all(Object.entries(importers).map(async ([relativePath, { dependencies }]) => {
64
+ var _a;
65
+ if (path_1.default.resolve(projectFolder, relativePath) === projectFolder) {
66
+ const dependenciesEntries = Object.entries(dependencies !== null && dependencies !== void 0 ? dependencies : {});
67
+ for (const [dependencyName, dependencyValue] of dependenciesEntries) {
68
+ const fullDependencyPath = (0, shrinkwrap_1.splicePackageWithVersion)(shrinkwrapFileMajorVersion, dependencyName, typeof dependencyValue === 'string'
69
+ ? dependencyValue
70
+ : dependencyValue.version);
71
+ if (fullDependencyPath.includes('link:')) {
72
+ const dependencyProject = this._rushConfiguration.getProjectByName(dependencyName);
73
+ if (dependencyProject && !((_a = this._checkedProjects) === null || _a === void 0 ? void 0 : _a.has(dependencyProject))) {
74
+ this._checkedProjects.add(project);
75
+ await this._searchAndValidateDependenciesAsync(dependencyProject, requiredVersions);
76
+ }
77
+ }
78
+ else {
79
+ await this._checkVersionCompatibilityAsync(shrinkwrapFileMajorVersion, packages, fullDependencyPath, requiredVersions, checkedDependencyPaths);
80
+ }
81
+ }
82
+ }
83
+ }));
84
+ }
85
+ async _performVersionRestrictionCheckAsync(requiredVersions, projectName) {
86
+ var _a;
87
+ try {
88
+ const project = (_a = this._rushConfiguration) === null || _a === void 0 ? void 0 : _a.getProjectByName(projectName);
89
+ if (!project) {
90
+ throw new Error(`Specified project "${projectName}" does not exist in ${common_1.LOCKFILE_LINT_JSON_FILENAME}`);
91
+ }
92
+ this._checkedProjects.add(project);
93
+ await this._searchAndValidateDependenciesAsync(project, requiredVersions);
94
+ return undefined;
95
+ }
96
+ catch (e) {
97
+ return e.message;
98
+ }
99
+ }
100
+ async onExecute() {
101
+ const rushConfiguration = rush_sdk_1.RushConfiguration.tryLoadFromDefaultLocation();
102
+ if (!rushConfiguration) {
103
+ throw new Error('The "lockfile-explorer check" must be executed in a folder that is under a Rush workspace folder');
104
+ }
105
+ this._rushConfiguration = rushConfiguration;
106
+ const lintingFile = path_1.default.resolve(this._rushConfiguration.commonFolder, 'config', common_1.LOCKFILE_EXPLORER_FOLDERNAME, common_1.LOCKFILE_LINT_JSON_FILENAME);
107
+ const { rules } = await node_core_library_1.JsonFile.loadAndValidateAsync(lintingFile, node_core_library_1.JsonSchema.fromLoadedObject(lockfile_lint_schema_json_1.default));
108
+ const issues = [];
109
+ await node_core_library_1.Async.forEachAsync(rules, async ({ requiredVersions, project, rule }) => {
110
+ switch (rule) {
111
+ case 'restrict-versions': {
112
+ const message = await this._performVersionRestrictionCheckAsync(requiredVersions, project);
113
+ if (message) {
114
+ issues.push({ project, rule, message });
115
+ }
116
+ break;
117
+ }
118
+ default: {
119
+ throw new Error('Unsupported rule name: ' + rule);
120
+ }
121
+ }
122
+ }, { concurrency: 50 });
123
+ if (issues.length > 0) {
124
+ this._terminal.writeLine();
125
+ // Deterministic order
126
+ for (const issue of issues.sort((a, b) => {
127
+ let diff = a.project.localeCompare(b.project);
128
+ if (diff !== 0) {
129
+ return diff;
130
+ }
131
+ diff = a.rule.localeCompare(b.rule);
132
+ if (diff !== 0) {
133
+ return diff;
134
+ }
135
+ return a.message.localeCompare(b.message);
136
+ })) {
137
+ this._terminal.writeLine(terminal_1.Colorize.red('PROBLEM: ') + terminal_1.Colorize.cyan(`[${issue.rule}] `) + issue.message + '\n');
138
+ }
139
+ throw new node_core_library_1.AlreadyReportedError();
140
+ }
141
+ this._terminal.writeLine(terminal_1.Colorize.green('SUCCESS: ') + 'All checks passed.');
142
+ }
143
+ }
144
+ exports.CheckAction = CheckAction;
145
+ //# sourceMappingURL=CheckAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckAction.js","sourceRoot":"","sources":["../../../../src/cli/lint/actions/CheckAction.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;;;;AAE3D,kDAA+D;AAC/D,gEAA+D;AAC/D,kDAAsG;AACtG,gDAAwB;AACxB,sDAA2B;AAC3B,oDAA4B;AAC5B,oEAA6G;AAE7G,2GAA4E;AAC5E,sDAAsG;AAEtG,0DAImC;AAmBnC,MAAa,WAAY,SAAQ,mCAAiB;IAOhD,YAAmB,MAA6B;QAC9C,KAAK,CAAC;YACJ,UAAU,EAAE,OAAO;YACnB,OAAO,EAAE,sDAAsD;YAC/D,aAAa,EACX,2DAA2D;gBAC3D,oCAA2B;gBAC3B,wDAAwD;SAC3D,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;QACvC,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,+BAA+B,CAC3C,0BAAkC,EAClC,QAA8B,EAC9B,cAAsB,EACtB,gBAAwC,EACxC,sBAAmC;;QAEnC,IAAI,QAAQ,IAAI,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YACxF,sBAAsB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC3C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAA,gCAAmB,EAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC;YAC1F,IAAI,IAAI,IAAI,gBAAgB,IAAI,CAAC,gBAAM,CAAC,SAAS,CAAC,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACnF,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,mBAAmB,gBAAgB,CAAC,IAAI,CAAC,IAAI;oBAClE,uBAAuB,OAAO,GAAG,CACpC,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,OAAO,CAAC,MAAA,QAAQ,CAAC,cAAc,CAAC,CAAC,YAAY,mCAAI,EAAE,CAAC,CAAC,GAAG,CAC7D,KAAK,EAAE,CAAC,qBAAqB,EAAE,wBAAwB,CAAC,EAAE,EAAE;gBAC1D,MAAM,IAAI,CAAC,+BAA+B,CACxC,0BAA0B,EAC1B,QAAQ,EACR,IAAA,qCAAwB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,wBAAwB,CACzB,EACD,gBAAgB,EAChB,sBAAsB,CACvB,CAAC;YACJ,CAAC,CACF,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,mCAAmC,CAC/C,OAAiC,EACjC,gBAAwC;QAExC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,qBAAqB,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC;QAEtE,MAAM,aAAa,GAAW,OAAO,CAAC,aAAa,CAAC;QACpD,MAAM,QAAQ,GAAa,OAAO,CAAC,QAAQ,CAAC;QAC5C,MAAM,kBAAkB,GAAW,QAAQ,CAAC,8BAA8B,EAAE,CAAC;QAC7E,IAAI,GAA0B,CAAC;QAC/B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACzC,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAE,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,gBAAgB,GAAW,MAAM,8BAAU,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;YACpF,GAAG,GAAG,iBAAI,CAAC,IAAI,CAAC,gBAAgB,CAA0B,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;QACrD,MAAM,0BAA0B,GAAW,IAAA,0CAA6B,EAAC,eAAe,CAAC,CAAC;QAC1F,MAAM,sBAAsB,GAAgB,IAAI,GAAG,EAAU,CAAC;QAE9D,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE;;YACvE,IAAI,cAAI,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,KAAK,aAAa,EAAE,CAAC;gBAChE,MAAM,mBAAmB,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,EAAE,CAAC,CAAC;gBAC/D,KAAK,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC,IAAI,mBAAmB,EAAE,CAAC;oBACpE,MAAM,kBAAkB,GAAG,IAAA,qCAAwB,EACjD,0BAA0B,EAC1B,cAAc,EACd,OAAO,eAAe,KAAK,QAAQ;wBACjC,CAAC,CAAC,eAAe;wBACjB,CAAC,CACG,eAID,CAAC,OAAO,CACd,CAAC;oBACF,IAAI,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;wBACzC,MAAM,iBAAiB,GACrB,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;wBAC3D,IAAI,iBAAiB,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,gBAAgB,0CAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA,EAAE,CAAC;4BACxE,IAAI,CAAC,gBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;4BACpC,MAAM,IAAI,CAAC,mCAAmC,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;wBACtF,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,CAAC,+BAA+B,CACxC,0BAA0B,EAC1B,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,CACvB,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,oCAAoC,CAChD,gBAAwC,EACxC,WAAmB;;QAEnB,IAAI,CAAC;YACH,MAAM,OAAO,GACX,MAAA,IAAI,CAAC,kBAAkB,0CAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CACb,sBAAsB,WAAW,uBAAuB,oCAA2B,EAAE,CACtF,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,IAAI,CAAC,mCAAmC,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;YAC1E,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,OAAO,CAAC;QACnB,CAAC;IACH,CAAC;IAES,KAAK,CAAC,SAAS;QACvB,MAAM,iBAAiB,GAAkC,4BAAiB,CAAC,0BAA0B,EAAE,CAAC;QACxG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,kBAAkB,GAAG,iBAAkB,CAAC;QAE7C,MAAM,WAAW,GAAW,cAAI,CAAC,OAAO,CACtC,IAAI,CAAC,kBAAkB,CAAC,YAAY,EACpC,QAAQ,EACR,qCAA4B,EAC5B,oCAA2B,CAC5B,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,GAAkB,MAAM,4BAAQ,CAAC,oBAAoB,CAClE,WAAW,EACX,8BAAU,CAAC,gBAAgB,CAAC,mCAAkB,CAAC,CAChD,CAAC;QACF,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,MAAM,yBAAK,CAAC,YAAY,CACtB,KAAK,EACL,KAAK,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;YAC5C,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,mBAAmB,CAAC,CAAC,CAAC;oBACzB,MAAM,OAAO,GAAuB,MAAM,IAAI,CAAC,oCAAoC,CACjF,gBAAgB,EAChB,OAAO,CACR,CAAC;oBACF,IAAI,OAAO,EAAE,CAAC;wBACZ,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;oBAC1C,CAAC;oBACD,MAAM;gBACR,CAAC;gBAED,OAAO,CAAC,CAAC,CAAC;oBACR,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;QACH,CAAC,EACD,EAAE,WAAW,EAAE,EAAE,EAAE,CACpB,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;YAE3B,sBAAsB;YACtB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAU,EAAE;gBAC/C,IAAI,IAAI,GAAW,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACtD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC5C,CAAC,CAAC,EAAE,CAAC;gBACH,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,mBAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,mBAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CACrF,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,wCAAoB,EAAE,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,mBAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAC/E,CAAC;CACF;AA7MD,kCA6MC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Colorize, type ITerminal } from '@rushstack/terminal';\nimport { CommandLineAction } from '@rushstack/ts-command-line';\nimport { RushConfiguration, type RushConfigurationProject, type Subspace } from '@rushstack/rush-sdk';\nimport path from 'path';\nimport yaml from 'js-yaml';\nimport semver from 'semver';\nimport { AlreadyReportedError, Async, FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library';\n\nimport lockfileLintSchema from '../../../schemas/lockfile-lint.schema.json';\nimport { LOCKFILE_EXPLORER_FOLDERNAME, LOCKFILE_LINT_JSON_FILENAME } from '../../../constants/common';\nimport type { LintCommandLineParser } from '../LintCommandLineParser';\nimport {\n getShrinkwrapFileMajorVersion,\n parseDependencyPath,\n splicePackageWithVersion\n} from '../../../utils/shrinkwrap';\nimport type { Lockfile, LockfileV6 } from '@pnpm/lockfile-types';\n\nexport interface ILintRule {\n rule: 'restrict-versions';\n project: string;\n requiredVersions: Record<string, string>;\n}\n\nexport interface ILockfileLint {\n rules: ILintRule[];\n}\n\nexport interface ILintIssue {\n project: string;\n rule: string;\n message: string;\n}\n\nexport class CheckAction extends CommandLineAction {\n private readonly _terminal: ITerminal;\n\n private _rushConfiguration!: RushConfiguration;\n private _checkedProjects: Set<RushConfigurationProject>;\n private _docMap: Map<string, Lockfile | LockfileV6>;\n\n public constructor(parser: LintCommandLineParser) {\n super({\n actionName: 'check',\n summary: 'Check and report dependency issues in your workspace',\n documentation:\n 'This command applies the policies that are configured in ' +\n LOCKFILE_LINT_JSON_FILENAME +\n ', reporting any problems found in your PNPM workspace.'\n });\n\n this._terminal = parser.globalTerminal;\n this._checkedProjects = new Set();\n this._docMap = new Map();\n }\n\n private async _checkVersionCompatibilityAsync(\n shrinkwrapFileMajorVersion: number,\n packages: Lockfile['packages'],\n dependencyPath: string,\n requiredVersions: Record<string, string>,\n checkedDependencyPaths: Set<string>\n ): Promise<void> {\n if (packages && packages[dependencyPath] && !checkedDependencyPaths.has(dependencyPath)) {\n checkedDependencyPaths.add(dependencyPath);\n const { name, version } = parseDependencyPath(shrinkwrapFileMajorVersion, dependencyPath);\n if (name in requiredVersions && !semver.satisfies(version, requiredVersions[name])) {\n throw new Error(\n `The version of \"${name}\" should match \"${requiredVersions[name]}\";` +\n ` actual version is \"${version}\"`\n );\n }\n\n await Promise.all(\n Object.entries(packages[dependencyPath].dependencies ?? {}).map(\n async ([dependencyPackageName, dependencyPackageVersion]) => {\n await this._checkVersionCompatibilityAsync(\n shrinkwrapFileMajorVersion,\n packages,\n splicePackageWithVersion(\n shrinkwrapFileMajorVersion,\n dependencyPackageName,\n dependencyPackageVersion\n ),\n requiredVersions,\n checkedDependencyPaths\n );\n }\n )\n );\n }\n }\n\n private async _searchAndValidateDependenciesAsync(\n project: RushConfigurationProject,\n requiredVersions: Record<string, string>\n ): Promise<void> {\n this._terminal.writeLine(`Checking project \"${project.packageName}\"`);\n\n const projectFolder: string = project.projectFolder;\n const subspace: Subspace = project.subspace;\n const shrinkwrapFilename: string = subspace.getCommittedShrinkwrapFilename();\n let doc: Lockfile | LockfileV6;\n if (this._docMap.has(shrinkwrapFilename)) {\n doc = this._docMap.get(shrinkwrapFilename)!;\n } else {\n const pnpmLockfileText: string = await FileSystem.readFileAsync(shrinkwrapFilename);\n doc = yaml.load(pnpmLockfileText) as Lockfile | LockfileV6;\n this._docMap.set(shrinkwrapFilename, doc);\n }\n const { importers, lockfileVersion, packages } = doc;\n const shrinkwrapFileMajorVersion: number = getShrinkwrapFileMajorVersion(lockfileVersion);\n const checkedDependencyPaths: Set<string> = new Set<string>();\n\n await Promise.all(\n Object.entries(importers).map(async ([relativePath, { dependencies }]) => {\n if (path.resolve(projectFolder, relativePath) === projectFolder) {\n const dependenciesEntries = Object.entries(dependencies ?? {});\n for (const [dependencyName, dependencyValue] of dependenciesEntries) {\n const fullDependencyPath = splicePackageWithVersion(\n shrinkwrapFileMajorVersion,\n dependencyName,\n typeof dependencyValue === 'string'\n ? dependencyValue\n : (\n dependencyValue as {\n version: string;\n specifier: string;\n }\n ).version\n );\n if (fullDependencyPath.includes('link:')) {\n const dependencyProject: RushConfigurationProject | undefined =\n this._rushConfiguration.getProjectByName(dependencyName);\n if (dependencyProject && !this._checkedProjects?.has(dependencyProject)) {\n this._checkedProjects!.add(project);\n await this._searchAndValidateDependenciesAsync(dependencyProject, requiredVersions);\n }\n } else {\n await this._checkVersionCompatibilityAsync(\n shrinkwrapFileMajorVersion,\n packages,\n fullDependencyPath,\n requiredVersions,\n checkedDependencyPaths\n );\n }\n }\n }\n })\n );\n }\n\n private async _performVersionRestrictionCheckAsync(\n requiredVersions: Record<string, string>,\n projectName: string\n ): Promise<string | undefined> {\n try {\n const project: RushConfigurationProject | undefined =\n this._rushConfiguration?.getProjectByName(projectName);\n if (!project) {\n throw new Error(\n `Specified project \"${projectName}\" does not exist in ${LOCKFILE_LINT_JSON_FILENAME}`\n );\n }\n this._checkedProjects.add(project);\n await this._searchAndValidateDependenciesAsync(project, requiredVersions);\n return undefined;\n } catch (e) {\n return e.message;\n }\n }\n\n protected async onExecute(): Promise<void> {\n const rushConfiguration: RushConfiguration | undefined = RushConfiguration.tryLoadFromDefaultLocation();\n if (!rushConfiguration) {\n throw new Error(\n 'The \"lockfile-explorer check\" must be executed in a folder that is under a Rush workspace folder'\n );\n }\n this._rushConfiguration = rushConfiguration!;\n\n const lintingFile: string = path.resolve(\n this._rushConfiguration.commonFolder,\n 'config',\n LOCKFILE_EXPLORER_FOLDERNAME,\n LOCKFILE_LINT_JSON_FILENAME\n );\n const { rules }: ILockfileLint = await JsonFile.loadAndValidateAsync(\n lintingFile,\n JsonSchema.fromLoadedObject(lockfileLintSchema)\n );\n const issues: ILintIssue[] = [];\n await Async.forEachAsync(\n rules,\n async ({ requiredVersions, project, rule }) => {\n switch (rule) {\n case 'restrict-versions': {\n const message: string | undefined = await this._performVersionRestrictionCheckAsync(\n requiredVersions,\n project\n );\n if (message) {\n issues.push({ project, rule, message });\n }\n break;\n }\n\n default: {\n throw new Error('Unsupported rule name: ' + rule);\n }\n }\n },\n { concurrency: 50 }\n );\n if (issues.length > 0) {\n this._terminal.writeLine();\n\n // Deterministic order\n for (const issue of issues.sort((a, b): number => {\n let diff: number = a.project.localeCompare(b.project);\n if (diff !== 0) {\n return diff;\n }\n diff = a.rule.localeCompare(b.rule);\n if (diff !== 0) {\n return diff;\n }\n return a.message.localeCompare(b.message);\n })) {\n this._terminal.writeLine(\n Colorize.red('PROBLEM: ') + Colorize.cyan(`[${issue.rule}] `) + issue.message + '\\n'\n );\n }\n\n throw new AlreadyReportedError();\n }\n this._terminal.writeLine(Colorize.green('SUCCESS: ') + 'All checks passed.');\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { CommandLineAction } from '@rushstack/ts-command-line';
2
+ import type { LintCommandLineParser } from '../LintCommandLineParser';
3
+ export declare class InitAction extends CommandLineAction {
4
+ private readonly _terminal;
5
+ constructor(parser: LintCommandLineParser);
6
+ protected onExecute(): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=InitAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InitAction.d.ts","sourceRoot":"","sources":["../../../../src/cli/lint/actions/InitAction.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAO/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAGtE,qBAAa,UAAW,SAAQ,iBAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAEnB,MAAM,EAAE,qBAAqB;cAWhC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CA8B3C"}