@rushstack/rush-buildxl-graph-plugin 5.168.0 → 5.169.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 (35) hide show
  1. package/lib-esm/DropBuildGraphPlugin.js +62 -0
  2. package/lib-esm/DropBuildGraphPlugin.js.map +1 -0
  3. package/lib-esm/GraphProcessor.js +141 -0
  4. package/lib-esm/GraphProcessor.js.map +1 -0
  5. package/lib-esm/debugGraphFiltering.js +92 -0
  6. package/lib-esm/debugGraphFiltering.js.map +1 -0
  7. package/lib-esm/dropGraph.js +50 -0
  8. package/lib-esm/dropGraph.js.map +1 -0
  9. package/lib-esm/index.js +5 -0
  10. package/lib-esm/index.js.map +1 -0
  11. package/lib-esm/schemas/rush-buildxl-graph-plugin.schema.json +15 -0
  12. package/package.json +34 -10
  13. package/rush-plugin-manifest.json +2 -2
  14. /package/{lib → lib-commonjs}/DropBuildGraphPlugin.js +0 -0
  15. /package/{lib → lib-commonjs}/DropBuildGraphPlugin.js.map +0 -0
  16. /package/{lib → lib-commonjs}/GraphProcessor.js +0 -0
  17. /package/{lib → lib-commonjs}/GraphProcessor.js.map +0 -0
  18. /package/{lib → lib-commonjs}/debugGraphFiltering.js +0 -0
  19. /package/{lib → lib-commonjs}/debugGraphFiltering.js.map +0 -0
  20. /package/{lib → lib-commonjs}/dropGraph.js +0 -0
  21. /package/{lib → lib-commonjs}/dropGraph.js.map +0 -0
  22. /package/{lib → lib-commonjs}/index.js +0 -0
  23. /package/{lib → lib-commonjs}/index.js.map +0 -0
  24. /package/{lib → lib-commonjs}/schemas/rush-buildxl-graph-plugin.schema.json +0 -0
  25. /package/{lib → lib-dts}/DropBuildGraphPlugin.d.ts +0 -0
  26. /package/{lib → lib-dts}/DropBuildGraphPlugin.d.ts.map +0 -0
  27. /package/{lib → lib-dts}/GraphProcessor.d.ts +0 -0
  28. /package/{lib → lib-dts}/GraphProcessor.d.ts.map +0 -0
  29. /package/{lib → lib-dts}/debugGraphFiltering.d.ts +0 -0
  30. /package/{lib → lib-dts}/debugGraphFiltering.d.ts.map +0 -0
  31. /package/{lib → lib-dts}/dropGraph.d.ts +0 -0
  32. /package/{lib → lib-dts}/dropGraph.d.ts.map +0 -0
  33. /package/{lib → lib-dts}/index.d.ts +0 -0
  34. /package/{lib → lib-dts}/index.d.ts.map +0 -0
  35. /package/{lib → lib-dts}/tsdoc-metadata.json +0 -0
@@ -0,0 +1,62 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { RushConstants } from '@rushstack/rush-sdk';
4
+ import { CommandLineParameterKind } from '@rushstack/ts-command-line';
5
+ const PLUGIN_NAME = 'DropBuildGraphPlugin';
6
+ const DROP_GRAPH_PARAMETER_LONG_NAME = '--drop-graph';
7
+ /**
8
+ * This plugin is used to drop the build graph to a file for BuildXL to consume.
9
+ * @public
10
+ */
11
+ export class DropBuildGraphPlugin {
12
+ constructor(options) {
13
+ this.pluginName = PLUGIN_NAME;
14
+ this._buildXLCommandNames = options.buildXLCommandNames;
15
+ }
16
+ apply(session, rushConfiguration) {
17
+ async function handleCreateOperationsForCommandAsync(commandName, operations, context) {
18
+ // TODO: Introduce an API to allow plugins to register command line options for arbitrary, existing commands
19
+ // in a repo
20
+ const dropGraphParameter = context.customParameters.get(DROP_GRAPH_PARAMETER_LONG_NAME);
21
+ if (!dropGraphParameter) {
22
+ // TODO: Introduce an API to allow plugins to register command line options for arbitrary, existing commands
23
+ // in a repo
24
+ throw new Error(`The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter needs to be defined in "${RushConstants.commandLineFilename}" ` +
25
+ `and associated with the action "${commandName}"`);
26
+ }
27
+ else if (dropGraphParameter.kind !== CommandLineParameterKind.String) {
28
+ throw new Error(`The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter must be a string parameter`);
29
+ }
30
+ const dropGraphPath = dropGraphParameter === null || dropGraphParameter === void 0 ? void 0 : dropGraphParameter.value;
31
+ if (dropGraphPath) {
32
+ const { dropGraphAsync } = await import('./dropGraph');
33
+ const isValid = await dropGraphAsync({
34
+ operations,
35
+ context,
36
+ dropGraphPath,
37
+ rushConfiguration,
38
+ logger: session.getLogger(PLUGIN_NAME)
39
+ });
40
+ if (!isValid) {
41
+ throw new Error('Failed to validate the graph');
42
+ }
43
+ else {
44
+ // If the --drop-graph flag is present, we want to exit the process after dropping the graph
45
+ return new Set();
46
+ }
47
+ }
48
+ else {
49
+ return operations;
50
+ }
51
+ }
52
+ for (const buildXLCommandName of this._buildXLCommandNames) {
53
+ session.hooks.runPhasedCommand.for(buildXLCommandName).tap(PLUGIN_NAME, (command) => {
54
+ command.hooks.createOperations.tapPromise({
55
+ name: PLUGIN_NAME,
56
+ stage: Number.MAX_SAFE_INTEGER // Run this after other plugins have created all operations
57
+ }, async (operations, context) => await handleCreateOperationsForCommandAsync(command.actionName, operations, context));
58
+ });
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=DropBuildGraphPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropBuildGraphPlugin.js","sourceRoot":"","sources":["../src/DropBuildGraphPlugin.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAE3D,OAAO,EACL,aAAa,EAOd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAmC,MAAM,4BAA4B,CAAC;AAIvG,MAAM,WAAW,GAA2B,sBAAsB,CAAC;AAuBnE,MAAM,8BAA8B,GAAmB,cAAc,CAAC;AAEtE;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAI/B,YAAmB,OAAgC;QAHnC,eAAU,GAAW,WAAW,CAAC;QAI/C,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAC1D,CAAC;IAEM,KAAK,CAAC,OAAoB,EAAE,iBAAoC;QACrE,KAAK,UAAU,qCAAqC,CAClD,WAAmB,EACnB,UAA0B,EAC1B,OAAiC;YAEjC,4GAA4G;YAC5G,YAAY;YACZ,MAAM,kBAAkB,GAA2C,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAC7F,8BAA8B,CACD,CAAC;YAChC,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,4GAA4G;gBAC5G,YAAY;gBACZ,MAAM,IAAI,KAAK,CACb,OAAO,8BAA8B,sCAAsC,aAAa,CAAC,mBAAmB,IAAI;oBAC9G,mCAAmC,WAAW,GAAG,CACpD,CAAC;YACJ,CAAC;iBAAM,IAAI,kBAAkB,CAAC,IAAI,KAAK,wBAAwB,CAAC,MAAM,EAAE,CAAC;gBACvE,MAAM,IAAI,KAAK,CAAC,OAAO,8BAA8B,uCAAuC,CAAC,CAAC;YAChG,CAAC;YAED,MAAM,aAAa,GAAuB,kBAAkB,aAAlB,kBAAkB,uBAAlB,kBAAkB,CAAE,KAAK,CAAC;YACpE,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;gBACvD,MAAM,OAAO,GAAY,MAAM,cAAc,CAAC;oBAC5C,UAAU;oBACV,OAAO;oBACP,aAAa;oBACb,iBAAiB;oBACjB,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC;iBACvC,CAAC,CAAC;gBAEH,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,4FAA4F;oBAC5F,OAAO,IAAI,GAAG,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,UAAU,CAAC;YACpB,CAAC;QACH,CAAC;QAED,KAAK,MAAM,kBAAkB,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC3D,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,OAAuB,EAAE,EAAE;gBAClG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,CACvC;oBACE,IAAI,EAAE,WAAW;oBACjB,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,2DAA2D;iBAC3F,EACD,KAAK,EAAE,UAA0B,EAAE,OAAiC,EAAE,EAAE,CACtE,MAAM,qCAAqC,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,CACvF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF","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 {\n RushConstants,\n type ICreateOperationsContext,\n type IPhasedCommand,\n type IRushPlugin,\n type Operation,\n type RushConfiguration,\n type RushSession\n} from '@rushstack/rush-sdk';\nimport { CommandLineParameterKind, type CommandLineStringParameter } from '@rushstack/ts-command-line';\n\nimport type { IGraphNode } from './GraphProcessor';\n\nconst PLUGIN_NAME: 'DropBuildGraphPlugin' = 'DropBuildGraphPlugin';\n\n/**\n * This is the type that represents the schema of the drop file\n * @public\n */\nexport interface IBuildXLRushGraph {\n nodes: IGraphNode[];\n repoSettings: {\n commonTempFolder: string;\n };\n}\n\n/**\n * @public\n */\nexport interface IDropGraphPluginOptions {\n /**\n * The names of the commands that will be used to run BuildXL\n */\n buildXLCommandNames: string[];\n}\n\nconst DROP_GRAPH_PARAMETER_LONG_NAME: '--drop-graph' = '--drop-graph';\n\n/**\n * This plugin is used to drop the build graph to a file for BuildXL to consume.\n * @public\n */\nexport class DropBuildGraphPlugin implements IRushPlugin {\n public readonly pluginName: string = PLUGIN_NAME;\n private readonly _buildXLCommandNames: string[];\n\n public constructor(options: IDropGraphPluginOptions) {\n this._buildXLCommandNames = options.buildXLCommandNames;\n }\n\n public apply(session: RushSession, rushConfiguration: RushConfiguration): void {\n async function handleCreateOperationsForCommandAsync(\n commandName: string,\n operations: Set<Operation>,\n context: ICreateOperationsContext\n ): Promise<Set<Operation>> {\n // TODO: Introduce an API to allow plugins to register command line options for arbitrary, existing commands\n // in a repo\n const dropGraphParameter: CommandLineStringParameter | undefined = context.customParameters.get(\n DROP_GRAPH_PARAMETER_LONG_NAME\n ) as CommandLineStringParameter;\n if (!dropGraphParameter) {\n // TODO: Introduce an API to allow plugins to register command line options for arbitrary, existing commands\n // in a repo\n throw new Error(\n `The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter needs to be defined in \"${RushConstants.commandLineFilename}\" ` +\n `and associated with the action \"${commandName}\"`\n );\n } else if (dropGraphParameter.kind !== CommandLineParameterKind.String) {\n throw new Error(`The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter must be a string parameter`);\n }\n\n const dropGraphPath: string | undefined = dropGraphParameter?.value;\n if (dropGraphPath) {\n const { dropGraphAsync } = await import('./dropGraph');\n const isValid: boolean = await dropGraphAsync({\n operations,\n context,\n dropGraphPath,\n rushConfiguration,\n logger: session.getLogger(PLUGIN_NAME)\n });\n\n if (!isValid) {\n throw new Error('Failed to validate the graph');\n } else {\n // If the --drop-graph flag is present, we want to exit the process after dropping the graph\n return new Set();\n }\n } else {\n return operations;\n }\n }\n\n for (const buildXLCommandName of this._buildXLCommandNames) {\n session.hooks.runPhasedCommand.for(buildXLCommandName).tap(PLUGIN_NAME, (command: IPhasedCommand) => {\n command.hooks.createOperations.tapPromise(\n {\n name: PLUGIN_NAME,\n stage: Number.MAX_SAFE_INTEGER // Run this after other plugins have created all operations\n },\n async (operations: Set<Operation>, context: ICreateOperationsContext) =>\n await handleCreateOperationsForCommandAsync(command.actionName, operations, context)\n );\n });\n }\n }\n}\n"]}
@@ -0,0 +1,141 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { Colorize } from '@rushstack/terminal';
4
+ const REQUIRED_FIELDS = [
5
+ // command is absent because it is not required
6
+ 'id',
7
+ 'task',
8
+ 'package',
9
+ 'dependencies',
10
+ 'workingDirectory'
11
+ ];
12
+ /*
13
+ * Get the operation id
14
+ */
15
+ export function getOperationId(operation) {
16
+ const { associatedPhase: { name: task }, associatedProject: { packageName } } = operation;
17
+ return `${packageName}#${task}`;
18
+ }
19
+ export class GraphProcessor {
20
+ constructor(logger) {
21
+ this._logger = logger;
22
+ }
23
+ /*
24
+ * Convert the operationMap into an array of graph nodes with empty commands pruned
25
+ */
26
+ processOperations(operations) {
27
+ const nodeMap = new Map();
28
+ for (const operation of operations) {
29
+ const entry = this._operationAsHashedEntry(operation);
30
+ nodeMap.set(entry.id, entry);
31
+ }
32
+ return this._pruneNoOps(nodeMap);
33
+ }
34
+ /*
35
+ * Validate that all dependencies exist
36
+ * Validate that all nodes have a non-empty command
37
+ * Print a message to the logger, and return true if the graph is valid
38
+ */
39
+ validateGraph(entries) {
40
+ const entryIDs = new Set(entries.map((entry) => entry.id));
41
+ let isValid = true;
42
+ for (const entry of entries) {
43
+ for (const depId of entry.dependencies) {
44
+ if (!entryIDs.has(depId)) {
45
+ this._logger.emitError(new Error(`${entry.id} has a dependency on ${depId} which does not exist`));
46
+ isValid = false;
47
+ }
48
+ }
49
+ if (!entry.command) {
50
+ this._logger.emitError(new Error(`There is an empty command in ${entry.id}`));
51
+ isValid = false;
52
+ }
53
+ }
54
+ if (isValid) {
55
+ this._logger.terminal.writeLine(Colorize.green('All nodes have non-empty commands and dependencies which exist'));
56
+ }
57
+ const totalEdges = entries.reduce((acc, entry) => acc + entry.dependencies.length, 0);
58
+ this._logger.terminal.writeLine(`Graph has ${entries.length} nodes, ${totalEdges} edges`);
59
+ return isValid;
60
+ }
61
+ /*
62
+ * remove all entries with empty commands
63
+ * if an entry has a dependency with an empty command, it should inherit the dependencies of the empty command
64
+ */
65
+ _pruneNoOps(inputNodeMap) {
66
+ // Cache for the non-empty upstream dependencies of each operation
67
+ const nonEmptyDependenciesByOperation = new Map();
68
+ function getNonEmptyDependencies(node) {
69
+ // If we've already computed this, return the cached result
70
+ let nonEmptyDependencies = nonEmptyDependenciesByOperation.get(node);
71
+ if (!nonEmptyDependencies) {
72
+ nonEmptyDependencies = new Set();
73
+ nonEmptyDependenciesByOperation.set(node, nonEmptyDependencies);
74
+ for (const dependencyID of node.dependencies) {
75
+ if (!inputNodeMap.get(dependencyID).command) {
76
+ // If the dependency is empty, recursively inherit its non-empty dependencies
77
+ for (const deepDependency of getNonEmptyDependencies(inputNodeMap.get(dependencyID))) {
78
+ nonEmptyDependencies.add(deepDependency);
79
+ }
80
+ }
81
+ else {
82
+ nonEmptyDependencies.add(dependencyID);
83
+ }
84
+ }
85
+ }
86
+ return nonEmptyDependencies;
87
+ }
88
+ const result = [];
89
+ for (const node of inputNodeMap.values()) {
90
+ const command = node.command;
91
+ if (command) {
92
+ const nonEmptyDependencySet = getNonEmptyDependencies(node);
93
+ result.push({
94
+ ...node,
95
+ dependencies: Array.from(nonEmptyDependencySet),
96
+ command: command
97
+ });
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ /*
103
+ * Convert an operation into a graph node
104
+ */
105
+ _operationAsHashedEntry(operation) {
106
+ const { runner, associatedPhase: { name: task }, associatedProject: {
107
+ // "package" is a reserved word
108
+ packageName, projectFolder: workingDirectory }, settings, dependencies: operationDependencies } = operation;
109
+ const dependencies = new Set();
110
+ for (const dependency of operationDependencies.values()) {
111
+ const id = getOperationId(dependency);
112
+ dependencies.add(id);
113
+ }
114
+ const node = {
115
+ id: getOperationId(operation),
116
+ task,
117
+ package: packageName,
118
+ dependencies,
119
+ workingDirectory,
120
+ command: runner === null || runner === void 0 ? void 0 : runner.commandToRun
121
+ };
122
+ if (settings === null || settings === void 0 ? void 0 : settings.disableBuildCacheForOperation) {
123
+ node.cacheable = false;
124
+ }
125
+ const missingFields = [];
126
+ for (const requiredField of REQUIRED_FIELDS) {
127
+ if (!node[requiredField]) {
128
+ missingFields.push(requiredField);
129
+ }
130
+ }
131
+ if (missingFields.length > 0) {
132
+ this._logger.emitError(new Error(`Operation is missing required fields ${missingFields.join(', ')}: ${JSON.stringify(node)}`));
133
+ }
134
+ // the runner is a no-op if and only if the command is empty
135
+ if (!!(runner === null || runner === void 0 ? void 0 : runner.isNoOp) !== !node.command) {
136
+ this._logger.emitError(new Error(`${node.id}: Operation runner isNoOp does not match commandToRun existence`));
137
+ }
138
+ return node;
139
+ }
140
+ }
141
+ //# sourceMappingURL=GraphProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GraphProcessor.js","sourceRoot":"","sources":["../src/GraphProcessor.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 type { Operation, ILogger } from '@rushstack/rush-sdk';\nimport type { ShellOperationRunner } from '@rushstack/rush-sdk/lib/logic/operations/ShellOperationRunner';\nimport { Colorize } from '@rushstack/terminal';\n\n/**\n * @example\n * ```\n * {\n * \"id\": \"@rushstack/node-core-library#_phase:build\",\n * \"task\": \"_phase:build\",\n * \"package\": \"@rushstack/node-core-library\",\n * \"dependencies\": [\n * \"@rushstack/eslint-patch#_phase:build\",\n * \"@rushstack/eslint-plugin#_phase:build\",\n * \"@rushstack/eslint-plugin-packlets#_phase:build\",\n * \"@rushstack/eslint-plugin-security#_phase:build\"\n * ],\n * \"workingDirectory\": \"/repo/libraries/node-core-library\",\n * \"command\": \"heft run --only build -- --clean --production --drop-graph ./src/examples/graph.json\"\n * }\n * ```\n *\n * See https://github.com/microsoft/BuildXL/blob/adf025c1b96b8106984928df3e9c30c8331bc8d6/Public/Src/Tools/JavaScript/Tool.RushGraphBuilder/src/RushBuildPluginGraph.ts\n *\n * @public\n */\nexport interface IGraphNode {\n /**\n * The unique id of the Pip\n *\n * @example\n * `@rushstack/node-core-library#_phase:build`\n */\n id: string;\n\n /**\n * The command to run during the Pip\n */\n command: string;\n\n /**\n * The working directory of the Pip\n */\n workingDirectory: string;\n\n /**\n * The project name associated with the Pip\n *\n * @example\n * `@rushstack/node-core-library`\n */\n package: string;\n\n /**\n * The task name of the Pip\n *\n * @example\n * `_phase:build`\n */\n task: string;\n\n /**\n * The IDs of the dependencies of the Pip. These are the {@link IGraphNode.id} properties of other Pips.\n */\n dependencies: string[];\n\n /**\n * If false, the Pip is uncacheable\n */\n cacheable?: false;\n}\n\ninterface IGraphNodeInternal extends Omit<IGraphNode, 'dependencies' | 'command'> {\n dependencies: ReadonlySet<string>;\n command: string | undefined;\n}\n\ntype NodeMap = ReadonlyMap<string, IGraphNodeInternal>;\n\nconst REQUIRED_FIELDS: Array<keyof IGraphNodeInternal> = [\n // command is absent because it is not required\n 'id',\n 'task',\n 'package',\n 'dependencies',\n 'workingDirectory'\n];\n\n/*\n * Get the operation id\n */\nexport function getOperationId(operation: Operation): string {\n const {\n associatedPhase: { name: task },\n associatedProject: { packageName }\n } = operation;\n return `${packageName}#${task}`;\n}\n\nexport class GraphProcessor {\n private readonly _logger: ILogger;\n\n public constructor(logger: ILogger) {\n this._logger = logger;\n }\n\n /*\n * Convert the operationMap into an array of graph nodes with empty commands pruned\n */\n public processOperations(operations: Set<Operation>): IGraphNode[] {\n const nodeMap: Map<string, IGraphNodeInternal> = new Map();\n for (const operation of operations) {\n const entry: IGraphNodeInternal = this._operationAsHashedEntry(operation);\n nodeMap.set(entry.id, entry);\n }\n\n return this._pruneNoOps(nodeMap);\n }\n\n /*\n * Validate that all dependencies exist\n * Validate that all nodes have a non-empty command\n * Print a message to the logger, and return true if the graph is valid\n */\n public validateGraph(entries: readonly Readonly<IGraphNode>[]): boolean {\n const entryIDs: Set<string> = new Set(entries.map((entry) => entry.id));\n let isValid: boolean = true;\n for (const entry of entries) {\n for (const depId of entry.dependencies) {\n if (!entryIDs.has(depId)) {\n this._logger.emitError(new Error(`${entry.id} has a dependency on ${depId} which does not exist`));\n isValid = false;\n }\n }\n\n if (!entry.command) {\n this._logger.emitError(new Error(`There is an empty command in ${entry.id}`));\n isValid = false;\n }\n }\n\n if (isValid) {\n this._logger.terminal.writeLine(\n Colorize.green('All nodes have non-empty commands and dependencies which exist')\n );\n }\n\n const totalEdges: number = entries.reduce((acc, entry) => acc + entry.dependencies.length, 0);\n this._logger.terminal.writeLine(`Graph has ${entries.length} nodes, ${totalEdges} edges`);\n return isValid;\n }\n\n /*\n * remove all entries with empty commands\n * if an entry has a dependency with an empty command, it should inherit the dependencies of the empty command\n */\n private _pruneNoOps(inputNodeMap: NodeMap): IGraphNode[] {\n // Cache for the non-empty upstream dependencies of each operation\n const nonEmptyDependenciesByOperation: Map<IGraphNodeInternal, Set<string>> = new Map();\n function getNonEmptyDependencies(node: IGraphNodeInternal): ReadonlySet<string> {\n // If we've already computed this, return the cached result\n let nonEmptyDependencies: Set<string> | undefined = nonEmptyDependenciesByOperation.get(node);\n if (!nonEmptyDependencies) {\n nonEmptyDependencies = new Set();\n nonEmptyDependenciesByOperation.set(node, nonEmptyDependencies);\n for (const dependencyID of node.dependencies) {\n if (!inputNodeMap.get(dependencyID)!.command) {\n // If the dependency is empty, recursively inherit its non-empty dependencies\n for (const deepDependency of getNonEmptyDependencies(inputNodeMap.get(dependencyID)!)) {\n nonEmptyDependencies.add(deepDependency);\n }\n } else {\n nonEmptyDependencies.add(dependencyID);\n }\n }\n }\n\n return nonEmptyDependencies;\n }\n\n const result: IGraphNode[] = [];\n for (const node of inputNodeMap.values()) {\n const command: string | undefined = node.command;\n if (command) {\n const nonEmptyDependencySet: ReadonlySet<string> = getNonEmptyDependencies(node);\n result.push({\n ...node,\n dependencies: Array.from(nonEmptyDependencySet),\n command: command\n });\n }\n }\n\n return result;\n }\n\n /*\n * Convert an operation into a graph node\n */\n private _operationAsHashedEntry(operation: Operation): IGraphNodeInternal {\n const {\n runner,\n associatedPhase: { name: task },\n associatedProject: {\n // \"package\" is a reserved word\n packageName,\n projectFolder: workingDirectory\n },\n settings,\n dependencies: operationDependencies\n } = operation;\n\n const dependencies: Set<string> = new Set();\n for (const dependency of operationDependencies.values()) {\n const id: string = getOperationId(dependency);\n dependencies.add(id);\n }\n\n const node: Partial<IGraphNodeInternal> = {\n id: getOperationId(operation),\n task,\n package: packageName,\n dependencies,\n workingDirectory,\n command: (runner as Partial<Pick<ShellOperationRunner, 'commandToRun'>>)?.commandToRun\n };\n\n if (settings?.disableBuildCacheForOperation) {\n node.cacheable = false;\n }\n\n const missingFields: (keyof IGraphNodeInternal)[] = [];\n for (const requiredField of REQUIRED_FIELDS) {\n if (!node[requiredField]) {\n missingFields.push(requiredField);\n }\n }\n\n if (missingFields.length > 0) {\n this._logger.emitError(\n new Error(`Operation is missing required fields ${missingFields.join(', ')}: ${JSON.stringify(node)}`)\n );\n }\n\n // the runner is a no-op if and only if the command is empty\n if (!!runner?.isNoOp !== !node.command) {\n this._logger.emitError(\n new Error(`${node.id}: Operation runner isNoOp does not match commandToRun existence`)\n );\n }\n\n return node as IGraphNodeInternal;\n }\n}\n"]}
@@ -0,0 +1,92 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { getOperationId } from './GraphProcessor';
4
+ const BANNED_KEYS = new Set([
5
+ 'packageJsonEditor',
6
+ '_packageJson',
7
+ '_subspaces',
8
+ 'subspace',
9
+ 'rushConfiguration',
10
+ '_rushConfiguration',
11
+ 'associatedParameters',
12
+ '_dependencyProjects'
13
+ ]);
14
+ const ALLOWED_KEYS = new Set([
15
+ 'associatedPhase',
16
+ 'name',
17
+ 'associatedProject',
18
+ 'packageName',
19
+ 'projectFolder',
20
+ 'dependencies',
21
+ 'runner',
22
+ 'commandToRun',
23
+ 'isNoOp'
24
+ ]);
25
+ /*
26
+ * Filter Rush's build graph to remove repetitive and unimportant information. Useful if the schema ever changes, or needs to.
27
+ * Due to the fact that it includes all properties that are not banned, it is not guaranteed to be stable across versions.
28
+ * Should not be part of the critical path.
29
+ * @param obj - the object to filter
30
+ * @param depth - the maximum depth to recurse
31
+ * @param simplify - if true, will replace embedded operations with their operation id
32
+ */
33
+ export function filterObjectForDebug(obj, depth = 10, simplify = false) {
34
+ const output = {};
35
+ for (const [key, value] of Object.entries(obj)) {
36
+ if (BANNED_KEYS.has(key)) {
37
+ output[key] = `${key} truncated`;
38
+ continue;
39
+ }
40
+ else if (typeof value === 'function') {
41
+ output[key] = `${key}()`;
42
+ continue;
43
+ }
44
+ else if (value instanceof Set) {
45
+ output[key] = filterObjectForDebug(Array.from(value), Math.min(depth - 1, 5), true);
46
+ continue;
47
+ }
48
+ else if (value instanceof Object) {
49
+ if (depth <= 0) {
50
+ output[key] = `${key} too deep`;
51
+ continue;
52
+ }
53
+ if (simplify) {
54
+ const operationId = getOperationId(value);
55
+ if (operationId) {
56
+ output[key] = operationId;
57
+ continue;
58
+ }
59
+ }
60
+ output[key] = filterObjectForDebug(value, depth - 1);
61
+ continue;
62
+ }
63
+ output[key] = value;
64
+ }
65
+ return output;
66
+ }
67
+ export function filterObjectForTesting(obj, depth = 10, ignoreSets = false) {
68
+ const output = {};
69
+ for (const [key, value] of Object.entries(obj)) {
70
+ if (!ALLOWED_KEYS.has(key) && !key.match(/^\d+$/)) {
71
+ continue;
72
+ }
73
+ else if (value instanceof Set) {
74
+ if (!ignoreSets) {
75
+ // Don't need sets inside sets
76
+ output[key] = Array.from(value).map((subValue) => filterObjectForTesting(subValue, Math.min(depth - 1, 5), true));
77
+ }
78
+ continue;
79
+ }
80
+ else if (value instanceof Object) {
81
+ if (depth <= 0) {
82
+ output[key] = `${key} too deep`;
83
+ continue;
84
+ }
85
+ output[key] = filterObjectForTesting(value, depth - 1, ignoreSets);
86
+ continue;
87
+ }
88
+ output[key] = value;
89
+ }
90
+ return output;
91
+ }
92
+ //# sourceMappingURL=debugGraphFiltering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugGraphFiltering.js","sourceRoot":"","sources":["../src/debugGraphFiltering.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 { getOperationId } from './GraphProcessor';\n\nconst BANNED_KEYS: ReadonlySet<string> = new Set([\n 'packageJsonEditor',\n '_packageJson',\n '_subspaces',\n 'subspace',\n 'rushConfiguration',\n '_rushConfiguration',\n 'associatedParameters',\n '_dependencyProjects'\n]);\n\nconst ALLOWED_KEYS: ReadonlySet<string> = new Set([\n 'associatedPhase',\n 'name',\n 'associatedProject',\n 'packageName',\n 'projectFolder',\n 'dependencies',\n 'runner',\n 'commandToRun',\n 'isNoOp'\n]);\n\n/*\n * Filter Rush's build graph to remove repetitive and unimportant information. Useful if the schema ever changes, or needs to.\n * Due to the fact that it includes all properties that are not banned, it is not guaranteed to be stable across versions.\n * Should not be part of the critical path.\n * @param obj - the object to filter\n * @param depth - the maximum depth to recurse\n * @param simplify - if true, will replace embedded operations with their operation id\n */\nexport function filterObjectForDebug(obj: object, depth: number = 10, simplify: boolean = false): object {\n const output: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (BANNED_KEYS.has(key)) {\n output[key] = `${key} truncated`;\n continue;\n } else if (typeof value === 'function') {\n output[key] = `${key}()`;\n continue;\n } else if (value instanceof Set) {\n output[key] = filterObjectForDebug(Array.from(value), Math.min(depth - 1, 5), true);\n continue;\n } else if (value instanceof Object) {\n if (depth <= 0) {\n output[key] = `${key} too deep`;\n continue;\n }\n\n if (simplify) {\n const operationId: string | undefined = getOperationId(value);\n if (operationId) {\n output[key] = operationId;\n continue;\n }\n }\n\n output[key] = filterObjectForDebug(value, depth - 1);\n continue;\n }\n\n output[key] = value;\n }\n\n return output;\n}\n\nexport function filterObjectForTesting(obj: object, depth: number = 10, ignoreSets: boolean = false): object {\n const output: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (!ALLOWED_KEYS.has(key) && !key.match(/^\\d+$/)) {\n continue;\n } else if (value instanceof Set) {\n if (!ignoreSets) {\n // Don't need sets inside sets\n output[key] = Array.from(value).map((subValue) =>\n filterObjectForTesting(subValue, Math.min(depth - 1, 5), true)\n );\n }\n\n continue;\n } else if (value instanceof Object) {\n if (depth <= 0) {\n output[key] = `${key} too deep`;\n continue;\n }\n\n output[key] = filterObjectForTesting(value, depth - 1, ignoreSets);\n continue;\n }\n\n output[key] = value;\n }\n\n return output;\n}\n"]}
@@ -0,0 +1,50 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import path from 'node:path';
4
+ import { JsonFile } from '@rushstack/node-core-library';
5
+ import { GraphProcessor } from './GraphProcessor';
6
+ import { filterObjectForDebug, filterObjectForTesting } from './debugGraphFiltering';
7
+ const DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME = 'DEBUG_RUSH_BUILD_GRAPH';
8
+ export async function dropGraphAsync(options) {
9
+ const { operations, context, dropGraphPath, rushConfiguration: { commonTempFolder }, logger } = options;
10
+ if (process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME]) {
11
+ let filterFn;
12
+ const debugValue = process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME];
13
+ switch (process.env.DEBUG_RUSH_BUILD_GRAPH) {
14
+ case 'test': {
15
+ filterFn = filterObjectForTesting;
16
+ break;
17
+ }
18
+ case 'full': {
19
+ filterFn = filterObjectForDebug;
20
+ break;
21
+ }
22
+ default: {
23
+ throw new Error(`Invalid value for ${DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME}: ${debugValue}`);
24
+ }
25
+ }
26
+ if (filterFn) {
27
+ const graphOut = [];
28
+ for (const operation of operations.keys()) {
29
+ graphOut.push(filterFn(operation));
30
+ }
31
+ const debugOutput = {
32
+ OperationMap: graphOut,
33
+ ICreateOperationsContext: filterFn(context)
34
+ };
35
+ const debugPathOut = `${path.dirname(dropGraphPath)}/debug-${path.basename(dropGraphPath)}`;
36
+ await JsonFile.saveAsync(debugOutput, debugPathOut, { ensureFolderExists: true });
37
+ }
38
+ }
39
+ const graphProcessor = new GraphProcessor(logger);
40
+ const nodes = graphProcessor.processOperations(operations);
41
+ const buildXLGraph = {
42
+ nodes,
43
+ repoSettings: {
44
+ commonTempFolder
45
+ }
46
+ };
47
+ await JsonFile.saveAsync(buildXLGraph, dropGraphPath, { ensureFolderExists: true });
48
+ return graphProcessor.validateGraph(buildXLGraph.nodes);
49
+ }
50
+ //# sourceMappingURL=dropGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropGraph.js","sourceRoot":"","sources":["../src/dropGraph.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAE3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,OAAO,EAAmB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAUrF,MAAM,mCAAmC,GAA6B,wBAAwB,CAAC;AAE/F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA0B;IAC7D,MAAM,EACJ,UAAU,EACV,OAAO,EACP,aAAa,EACb,iBAAiB,EAAE,EAAE,gBAAgB,EAAE,EACvC,MAAM,EACP,GAAG,OAAO,CAAC;IACZ,IAAI,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,EAAE,CAAC;QACrD,IAAI,QAA+D,CAAC;QACpE,MAAM,UAAU,GAAuB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACxF,QAAQ,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC;YAC3C,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,QAAQ,GAAG,sBAAsB,CAAC;gBAClC,MAAM;YACR,CAAC;YAED,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,QAAQ,GAAG,oBAAoB,CAAC;gBAChC,MAAM;YACR,CAAC;YAED,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,IAAI,KAAK,CAAC,qBAAqB,mCAAmC,KAAK,UAAU,EAAE,CAAC,CAAC;YAC7F,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAc,EAAE,CAAC;YAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC1C,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;YACrC,CAAC;YAED,MAAM,WAAW,GAAY;gBAC3B,YAAY,EAAE,QAAQ;gBACtB,wBAAwB,EAAE,QAAQ,CAAC,OAAO,CAAC;aAC5C,CAAC;YACF,MAAM,YAAY,GAAW,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACpG,MAAM,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAmB,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,KAAK,GAAiB,cAAc,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,YAAY,GAAsB;QACtC,KAAK;QACL,YAAY,EAAE;YACZ,gBAAgB;SACjB;KACF,CAAC;IAEF,MAAM,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE,aAAa,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IACpF,OAAO,cAAc,CAAC,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC","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 path from 'node:path';\n\nimport type { ICreateOperationsContext, ILogger, Operation, RushConfiguration } from '@rushstack/rush-sdk';\nimport { JsonFile } from '@rushstack/node-core-library';\n\nimport type { IBuildXLRushGraph } from './DropBuildGraphPlugin';\nimport { type IGraphNode, GraphProcessor } from './GraphProcessor';\nimport { filterObjectForDebug, filterObjectForTesting } from './debugGraphFiltering';\n\nexport interface IDropGraphOptions {\n operations: Set<Operation>;\n context: ICreateOperationsContext;\n dropGraphPath: string;\n rushConfiguration: RushConfiguration;\n logger: ILogger;\n}\n\nconst DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME: 'DEBUG_RUSH_BUILD_GRAPH' = 'DEBUG_RUSH_BUILD_GRAPH';\n\nexport async function dropGraphAsync(options: IDropGraphOptions): Promise<boolean> {\n const {\n operations,\n context,\n dropGraphPath,\n rushConfiguration: { commonTempFolder },\n logger\n } = options;\n if (process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME]) {\n let filterFn: ((obj: object, depth?: number) => object) | undefined;\n const debugValue: string | undefined = process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME];\n switch (process.env.DEBUG_RUSH_BUILD_GRAPH) {\n case 'test': {\n filterFn = filterObjectForTesting;\n break;\n }\n\n case 'full': {\n filterFn = filterObjectForDebug;\n break;\n }\n\n default: {\n throw new Error(`Invalid value for ${DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME}: ${debugValue}`);\n }\n }\n\n if (filterFn) {\n const graphOut: unknown[] = [];\n for (const operation of operations.keys()) {\n graphOut.push(filterFn(operation));\n }\n\n const debugOutput: unknown = {\n OperationMap: graphOut,\n ICreateOperationsContext: filterFn(context)\n };\n const debugPathOut: string = `${path.dirname(dropGraphPath)}/debug-${path.basename(dropGraphPath)}`;\n await JsonFile.saveAsync(debugOutput, debugPathOut, { ensureFolderExists: true });\n }\n }\n\n const graphProcessor: GraphProcessor = new GraphProcessor(logger);\n const nodes: IGraphNode[] = graphProcessor.processOperations(operations);\n const buildXLGraph: IBuildXLRushGraph = {\n nodes,\n repoSettings: {\n commonTempFolder\n }\n };\n\n await JsonFile.saveAsync(buildXLGraph, dropGraphPath, { ensureFolderExists: true });\n return graphProcessor.validateGraph(buildXLGraph.nodes);\n}\n"]}
@@ -0,0 +1,5 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
+ // See LICENSE in the project root for license information.
3
+ import { DropBuildGraphPlugin } from './DropBuildGraphPlugin';
4
+ export default DropBuildGraphPlugin;
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2DAA2D;AAK3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,eAAe,oBAAoB,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nexport type { IBuildXLRushGraph } from './DropBuildGraphPlugin';\nexport type { IGraphNode } from './GraphProcessor';\nexport type { IDropGraphPluginOptions } from './DropBuildGraphPlugin';\nimport { DropBuildGraphPlugin } from './DropBuildGraphPlugin';\nexport default DropBuildGraphPlugin;\n"]}
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "Configuration for the BuildXL graph drop plugin.",
4
+ "type": "object",
5
+ "required": ["buildXLCommandNames"],
6
+ "properties": {
7
+ "buildXLCommandNames": {
8
+ "type": "array",
9
+ "description": "The names of the commands that will be used to run BuildXL",
10
+ "items": {
11
+ "type": "string"
12
+ }
13
+ }
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rushstack/rush-buildxl-graph-plugin",
3
- "version": "5.168.0",
3
+ "version": "5.169.0",
4
4
  "description": "Rush plugin for generating a BuildXL graph.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,21 +8,45 @@
8
8
  "directory": "rush-plugins/rush-buildxl-graph-plugin"
9
9
  },
10
10
  "homepage": "https://rushjs.io",
11
- "main": "lib/index.js",
12
- "types": "lib/index.d.ts",
11
+ "main": "./lib-commonjs/index.js",
12
+ "module": "./lib-esm/index.js",
13
+ "types": "./lib-dts/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./lib-dts/index.d.ts",
17
+ "import": "./lib-esm/index.js",
18
+ "require": "./lib-commonjs/index.js"
19
+ },
20
+ "./lib/*.schema.json": "./lib-commonjs/*.schema.json",
21
+ "./lib/*": {
22
+ "types": "./lib-dts/*.d.ts",
23
+ "import": "./lib-esm/*.js",
24
+ "require": "./lib-commonjs/*.js"
25
+ },
26
+ "./rush-plugin-manifest.json": "./rush-plugin-manifest.json",
27
+ "./package.json": "./package.json"
28
+ },
29
+ "typesVersions": {
30
+ "*": {
31
+ "lib/*": [
32
+ "lib-dts/*"
33
+ ]
34
+ }
35
+ },
13
36
  "license": "MIT",
14
37
  "dependencies": {
15
- "@rushstack/terminal": "0.21.0",
16
- "@rushstack/node-core-library": "5.19.1",
17
- "@rushstack/rush-sdk": "5.168.0",
18
- "@rushstack/ts-command-line": "5.2.0"
38
+ "@rushstack/node-core-library": "5.20.0",
39
+ "@rushstack/ts-command-line": "5.3.0",
40
+ "@rushstack/rush-sdk": "5.169.0",
41
+ "@rushstack/terminal": "0.22.0"
19
42
  },
20
43
  "devDependencies": {
21
44
  "eslint": "~9.37.0",
22
- "@microsoft/rush-lib": "5.168.0",
23
- "local-node-rig": "1.0.0",
24
- "@rushstack/heft": "1.1.14"
45
+ "@microsoft/rush-lib": "5.169.0",
46
+ "@rushstack/heft": "1.2.0",
47
+ "local-node-rig": "1.0.0"
25
48
  },
49
+ "sideEffects": false,
26
50
  "scripts": {
27
51
  "build": "heft build --clean",
28
52
  "start": "heft test --clean --watch",
@@ -4,8 +4,8 @@
4
4
  {
5
5
  "pluginName": "rush-buildxl-graph-plugin",
6
6
  "description": "Plugin for providing access to Rush build graph.",
7
- "entryPoint": "lib/index.js",
8
- "optionsSchema": "lib/schemas/rush-buildxl-graph-plugin.schema.json"
7
+ "entryPoint": "./lib-commonjs/index.js",
8
+ "optionsSchema": "./lib-commonjs/schemas/rush-buildxl-graph-plugin.schema.json"
9
9
  }
10
10
  ]
11
11
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes