Skip to content

Commit b8d06c8

Browse files
authored
[lockfile-explorer] Move graph generation logic to the server process (#5358)
* Enable packlets mixin * Move shared code into a packlet * - Rename `LockfileEntry` -> `LfxGraphEntry` - Rename `LockfileDependency` -> `LfxGraphDependency` - Rename `DependencyKind` -> `LfxDependencyKind` and simplify the enum members - Rename `LockfileEntryFilter` -> `LfxGraphEntryKind` - Eliminate circular dependency in IJsonLfxGraph.ts * Move lfxGraphLoader.ts from client to server * Move "@lifaon/path" dependency from client to server (we'll probably eliminate it entirely soon) * Integrate IJsonLfxWorkspace as part of the graph * Copy the "lfx-shared" because lockfile-explorer-web is a devDependency * Add "tslib" dependency for the server * Wire up readLockfileAsync() again * Revert launch.json change * rush change * Include new files in published package * Delete the obsolete /api/lockfile endpoint * Add unit tests * Enable Rush Stack lint rules for "lockfile-explorer" project * Move the convertLockfileV6DepPathToV5DepPath() kludge down into lfxGraphLoader.generateLockfileGraph() so the test now passes * Sort YAML to make snapshots more stable * Upgrade "js-yaml" from "~3.13.1" to "~4.1.0" * Fix up callers to use the new safe-by-default API * Regenerate snapshot, eliminating `!<tag:yaml.org,2002:js/undefined>` garbage * rush change * Regenerate snapshot * Rebuild all * PR feedback * PR feedback: move the copied packlet to build/lfx-shared (instead of temp/lfx-shared) * Improve test naming convention to better accommodate future test scenarios
1 parent 39a0b6b commit b8d06c8

67 files changed

Lines changed: 1749 additions & 529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ common/autoinstallers/*/.npmrc
107107
*.lock
108108

109109
# Common toolchain intermediate files
110+
build/
110111
temp/
111112
lib/
112113
lib-amd/

apps/api-documenter/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"@rushstack/node-core-library": "workspace:*",
2626
"@rushstack/terminal": "workspace:*",
2727
"@rushstack/ts-command-line": "workspace:*",
28-
"js-yaml": "~3.13.1",
28+
"js-yaml": "~4.1.0",
2929
"resolve": "~1.22.1"
3030
},
3131
"devDependencies": {
3232
"@rushstack/heft": "workspace:*",
33-
"@types/js-yaml": "3.12.1",
33+
"@types/js-yaml": "4.0.9",
3434
"@types/resolve": "1.20.2",
3535
"eslint": "~9.25.1",
3636
"local-node-rig": "workspace:*"

apps/api-documenter/src/documenters/OfficeYamlDocumenter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ export class OfficeYamlDocumenter extends YamlDocumenter {
4848
console.log('Loading snippets from ' + snippetsFilePath);
4949

5050
const snippetsContent: string = FileSystem.readFile(snippetsFilePath);
51-
this._snippets = yaml.load(snippetsContent, { filename: snippetsFilePath });
52-
this._snippetsAll = yaml.load(snippetsContent, { filename: snippetsFilePath });
51+
this._snippets = yaml.load(snippetsContent, { filename: snippetsFilePath }) as ISnippetsFile;
52+
this._snippetsAll = yaml.load(snippetsContent, { filename: snippetsFilePath }) as ISnippetsFile;
5353
}
5454

5555
/** @override */

apps/api-documenter/src/documenters/YamlDocumenter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ export class YamlDocumenter {
756756
): void {
757757
JsonFile.validateNoUndefinedMembers(dataObject);
758758

759-
let stringified: string = yaml.safeDump(dataObject, {
759+
let stringified: string = yaml.dump(dataObject, {
760760
lineWidth: 120
761761
});
762762

apps/api-documenter/src/utils/ToSdpConvertHelper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ function convert(inputPath: string, outputPath: string): void {
4949

5050
console.log(`convert file ${fpath} from udp to sdp`);
5151

52-
const file: IYamlApiFile = yaml.safeLoad(yamlContent) as IYamlApiFile;
52+
const file: IYamlApiFile = yaml.load(yamlContent) as IYamlApiFile;
5353
const result: { model: CommonYamlModel; type: string } | undefined = convertToSDP(file);
5454
if (result && result.model) {
55-
const stringified: string = `### YamlMime:TS${result.type}\n${yaml.safeDump(result.model, {
55+
const stringified: string = `### YamlMime:TS${result.type}\n${yaml.dump(result.model, {
5656
lineWidth: 120
5757
})}`;
5858
FileSystem.writeFile(`${outputPath}/${name}`, stringified, {

apps/lockfile-explorer-web/eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
const webAppProfile = require('local-web-rig/profiles/app/includes/eslint/flat/profile/web-app');
55
const reactMixin = require('local-web-rig/profiles/app/includes/eslint/flat/mixins/react');
6+
const packletsMixin = require('local-web-rig/profiles/app/includes/eslint/flat/mixins/packlets');
67

78
module.exports = [
89
...webAppProfile,
910
...reactMixin,
11+
packletsMixin,
1012
{
1113
files: ['**/*.ts', '**/*.tsx'],
1214
languageOptions: {

apps/lockfile-explorer-web/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
"_phase:test": "heft run --only test -- --clean"
1313
},
1414
"dependencies": {
15-
"@lifaon/path": "~2.1.0",
1615
"@reduxjs/toolkit": "~1.8.6",
1716
"@rushstack/rush-themed-ui": "workspace:*",
1817
"react-dom": "~17.0.2",

apps/lockfile-explorer-web/src/containers/BookmarksSidebar/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React, { useCallback } from 'react';
55
import appStyles from '../../App.scss';
66
import styles from './styles.scss';
77
import { useAppDispatch, useAppSelector } from '../../store/hooks';
8-
import type { LockfileEntry } from '../../parsing/LfxGraph';
8+
import type { LfxGraphEntry } from '../../packlets/lfx-shared';
99
import { clearStackAndPush, removeBookmark } from '../../store/slices/entrySlice';
1010
import { Button, ScrollArea, Text } from '@rushstack/rush-themed-ui';
1111

@@ -14,13 +14,13 @@ export const BookmarksSidebar = (): JSX.Element => {
1414
const dispatch = useAppDispatch();
1515

1616
const clear = useCallback(
17-
(entry: LockfileEntry) => () => {
17+
(entry: LfxGraphEntry) => () => {
1818
dispatch(clearStackAndPush(entry));
1919
},
2020
[dispatch]
2121
);
2222
const deleteEntry = useCallback(
23-
(entry: LockfileEntry) => () => {
23+
(entry: LfxGraphEntry) => () => {
2424
dispatch(removeBookmark(entry));
2525
},
2626
[dispatch]

apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
import React, { useCallback, useEffect, useState } from 'react';
55
import { ScrollArea, Text } from '@rushstack/rush-themed-ui';
6+
67
import styles from './styles.scss';
78
import appStyles from '../../App.scss';
8-
import { DependencyKind, type LockfileDependency } from '../../parsing/LfxGraph';
9+
10+
import { LfxDependencyKind, type LfxGraphDependency, type LfxGraphEntry } from '../../packlets/lfx-shared';
911
import { readPackageJsonAsync } from '../../helpers/lfxApiClient';
1012
import { useAppDispatch, useAppSelector } from '../../store/hooks';
1113
import { pushToStack, selectCurrentEntry } from '../../store/slices/entrySlice';
1214
import { ReactNull } from '../../types/ReactNull';
13-
import type { LockfileEntry } from '../../parsing/LfxGraph';
1415
import { logDiagnosticInfo } from '../../helpers/logDiagnosticInfo';
1516
import { displaySpecChanges } from '../../helpers/displaySpecChanges';
1617
import type { IPackageJson } from '../../types/IPackageJson';
@@ -27,7 +28,7 @@ enum DependencyKey {
2728
}
2829

2930
interface IInfluencerType {
30-
entry: LockfileEntry;
31+
entry: LfxGraphEntry;
3132
type: DependencyType;
3233
}
3334

@@ -36,14 +37,14 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
3637
const specChanges = useAppSelector((state) => state.workspace.specChanges);
3738
const dispatch = useAppDispatch();
3839

39-
const [inspectDependency, setInspectDependency] = useState<LockfileDependency | null>(null);
40+
const [inspectDependency, setInspectDependency] = useState<LfxGraphDependency | null>(null);
4041
const [influencers, setInfluencers] = useState<IInfluencerType[]>([]);
4142
const [directRefsPackageJSON, setDirectRefsPackageJSON] = useState<Map<string, IPackageJson | undefined>>(
4243
new Map()
4344
);
4445

4546
useEffect(() => {
46-
async function loadPackageJson(referrers: LockfileEntry[]): Promise<void> {
47+
async function loadPackageJson(referrers: LfxGraphEntry[]): Promise<void> {
4748
const referrersJsonMap = new Map<string, IPackageJson | undefined>();
4849
await Promise.all(
4950
referrers.map(async (ref) => {
@@ -80,15 +81,15 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
8081

8182
// Check if we need to calculate influencers.
8283
// If the current dependencyToTrace is a peer dependency then we do
83-
if (dependencyToTrace.dependencyType !== DependencyKind.PEER_DEPENDENCY) {
84+
if (dependencyToTrace.dependencyType !== LfxDependencyKind.Peer) {
8485
return;
8586
}
8687

8788
// calculate influencers
8889
const stack = [selectedEntry];
89-
const determinants = new Set<LockfileEntry>();
90-
const transitiveReferrers = new Set<LockfileEntry>();
91-
const visitedNodes = new Set<LockfileEntry>();
90+
const determinants = new Set<LfxGraphEntry>();
91+
const transitiveReferrers = new Set<LfxGraphEntry>();
92+
const visitedNodes = new Set<LfxGraphEntry>();
9293
visitedNodes.add(selectedEntry);
9394
while (stack.length) {
9495
const currEntry = stack.pop();
@@ -179,7 +180,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
179180
package.json spec:{' '}
180181
</Text>
181182
<Text type="span">
182-
{inspectDependency.dependencyType === DependencyKind.PEER_DEPENDENCY
183+
{inspectDependency.dependencyType === LfxDependencyKind.Peer
183184
? `"${inspectDependency.peerDependencyMeta.version}" ${
184185
inspectDependency.peerDependencyMeta.optional ? 'Optional' : 'Required'
185186
} Peer`
@@ -203,17 +204,15 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
203204

204205
const renderPeerDependencies = (): JSX.Element | ReactNull => {
205206
if (!selectedEntry) return ReactNull;
206-
const peerDeps = selectedEntry.dependencies.filter(
207-
(d) => d.dependencyType === DependencyKind.PEER_DEPENDENCY
208-
);
207+
const peerDeps = selectedEntry.dependencies.filter((d) => d.dependencyType === LfxDependencyKind.Peer);
209208
if (!peerDeps.length) {
210209
return (
211210
<div className={`${appStyles.ContainerCard} ${styles.InfluencerList}`}>
212211
<Text type="h5">No peer dependencies.</Text>
213212
</div>
214213
);
215214
}
216-
if (!inspectDependency || inspectDependency.dependencyType !== DependencyKind.PEER_DEPENDENCY) {
215+
if (!inspectDependency || inspectDependency.dependencyType !== LfxDependencyKind.Peer) {
217216
return (
218217
<div className={`${appStyles.ContainerCard} ${styles.InfluencerList}`}>
219218
<Text type="h5">Select a peer dependency to view its influencers</Text>
@@ -301,7 +300,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
301300
</Text>
302301
<div className={styles.DependencyListWrapper}>
303302
<ScrollArea>
304-
{selectedEntry.referrers?.map((referrer: LockfileEntry) => (
303+
{selectedEntry.referrers?.map((referrer: LfxGraphEntry) => (
305304
<div
306305
className={styles.DependencyItem}
307306
key={referrer.rawEntryId}
@@ -328,7 +327,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
328327
</Text>
329328
<div className={styles.DependencyListWrapper}>
330329
<ScrollArea>
331-
{selectedEntry.dependencies?.map((dependency: LockfileDependency) => (
330+
{selectedEntry.dependencies?.map((dependency: LfxGraphDependency) => (
332331
<div
333332
className={`${styles.DependencyItem} ${
334333
inspectDependency?.entryId === dependency.entryId && styles.SelectedDependencyItem
@@ -338,7 +337,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
338337
>
339338
<Text type="h5" bold>
340339
Name: {dependency.name}{' '}
341-
{dependency.dependencyType === DependencyKind.PEER_DEPENDENCY
340+
{dependency.dependencyType === LfxDependencyKind.Peer
342341
? `${
343342
dependency.peerDependencyMeta.optional ? '(Optional)' : '(Non-optional)'
344343
} Peer Dependency`

0 commit comments

Comments
 (0)