Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/lockfile-explorer-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@reduxjs/toolkit": "~1.8.6",
"@rushstack/rush-themed-ui": "workspace:*",
"prism-react-renderer": "~2.4.1",
"react-dom": "~17.0.2",
"react-redux": "~8.0.4",
"react": "~17.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import React from 'react';

import { Highlight, themes } from 'prism-react-renderer';

// Generate this list by doing console.log(Object.keys(Prism.languages))
// BUT THEN DELETE the APIs that are bizarrely mixed into this namespace:
// "extend", "insertBefore", "DFS"
export type PrismLanguage =
| 'plain'
| 'plaintext'
| 'text'
| 'txt'
| 'markup'
| 'html'
| 'mathml'
| 'svg'
| 'xml'
| 'ssml'
| 'atom'
| 'rss'
| 'regex'
| 'clike'
| 'javascript'
| 'js'
| 'actionscript'
| 'coffeescript'
| 'coffee'
| 'javadoclike'
| 'css'
| 'yaml'
| 'yml'
| 'markdown'
| 'md'
| 'graphql'
| 'sql'
| 'typescript'
| 'ts'
| 'jsdoc'
| 'flow'
| 'n4js'
| 'n4jsd'
| 'jsx'
| 'tsx'
| 'swift'
| 'kotlin'
| 'kt'
| 'kts'
| 'c'
| 'objectivec'
| 'objc'
| 'reason'
| 'rust'
| 'go'
| 'cpp'
| 'python'
| 'py'
| 'json'
| 'webmanifest';
Comment on lines +11 to +61
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export type PrismLanguage =
| 'plain'
| 'plaintext'
| 'text'
| 'txt'
| 'markup'
| 'html'
| 'mathml'
| 'svg'
| 'xml'
| 'ssml'
| 'atom'
| 'rss'
| 'regex'
| 'clike'
| 'javascript'
| 'js'
| 'actionscript'
| 'coffeescript'
| 'coffee'
| 'javadoclike'
| 'css'
| 'yaml'
| 'yml'
| 'markdown'
| 'md'
| 'graphql'
| 'sql'
| 'typescript'
| 'ts'
| 'jsdoc'
| 'flow'
| 'n4js'
| 'n4jsd'
| 'jsx'
| 'tsx'
| 'swift'
| 'kotlin'
| 'kt'
| 'kts'
| 'c'
| 'objectivec'
| 'objc'
| 'reason'
| 'rust'
| 'go'
| 'cpp'
| 'python'
| 'py'
| 'json'
| 'webmanifest';
export type PrismLanguage = Omit<import('prism-react-renderer').Prism.languages, "extend" | "insertBefore" | "DFS">

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These strings are not expressed in the Prism .d.ts file. They are apparently only available at runtime.


export const CodeBox = (props: { code: string; language: PrismLanguage }): JSX.Element => {
return (
<Highlight theme={themes.vsLight} code={props.code} language={props.language}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre style={style}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// See LICENSE in the project root for license information.

import React, { useCallback, useEffect, useState } from 'react';

import { readPnpmfileAsync, readPackageSpecAsync, readPackageJsonAsync } from '../../helpers/lfxApiClient';
import styles from './styles.scss';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { selectCurrentEntry } from '../../store/slices/entrySlice';
import type { IPackageJson } from '../../types/IPackageJson';
Expand All @@ -13,6 +13,9 @@ import { displaySpecChanges } from '../../helpers/displaySpecChanges';
import { isEntryModified } from '../../helpers/isEntryModified';
import { ScrollArea, Tabs, Text } from '@rushstack/rush-themed-ui';
import { LfxGraphEntryKind } from '../../packlets/lfx-shared';
import { CodeBox } from './CodeBox';

import styles from './styles.scss';

const PackageView: { [key: string]: string } = {
PACKAGE_JSON: 'PACKAGE_JSON',
Expand Down Expand Up @@ -48,9 +51,9 @@ export const PackageJsonViewer = (): JSX.Element => {

useEffect(() => {
async function loadPackageDetailsAsync(packageName: string): Promise<void> {
const packageJSONFile = await readPackageJsonAsync(packageName);
const packageJSONFile: IPackageJson | undefined = await readPackageJsonAsync(packageName);
setPackageJSON(packageJSONFile);
const parsedJSON = await readPackageSpecAsync(packageName);
const parsedJSON: IPackageJson | undefined = await readPackageSpecAsync(packageName);
setParsedPackageJSON(parsedJSON);
Comment on lines +54 to 57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parallelize these with Promise.all?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably premature optimization at this point.


if (packageJSONFile && parsedJSON) {
Expand Down Expand Up @@ -161,7 +164,7 @@ export const PackageJsonViewer = (): JSX.Element => {
Please select a Project or Package to view it&apos;s package.json
</Text>
);
return <pre>{JSON.stringify(packageJSON, null, 2)}</pre>;
return <CodeBox code={JSON.stringify(packageJSON, null, 2)} language="json" />;
case PackageView.PACKAGE_SPEC:
if (!pnpmfile) {
return (
Expand All @@ -171,7 +174,7 @@ export const PackageJsonViewer = (): JSX.Element => {
</Text>
);
}
return <pre>{pnpmfile}</pre>;
return <CodeBox code={pnpmfile} language="js" />;
case PackageView.PARSED_PACKAGE_JSON:
if (!parsedPackageJSON)
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ export interface IJsonLfxWorkspaceRushConfig {
* Otherwise this will be an empty string.
*/
readonly subspaceName: string;

/**
* The path to Rush's input file `.pnpmfile.cjs`, relative to `workspaceRootFullPath`
* and normalized to use forward slashes without a leading slash. In a Rush workspace,
* {@link IJsonLfxWorkspace.pnpmfilePath} is a temporary file that is generated from `rushPnpmfilePath`.
*
* @example `"common/config/my-subspace/pnpm-lock.yaml"`
*/
readonly rushPnpmfilePath: string;
}

export interface IJsonLfxWorkspace {
Expand Down Expand Up @@ -44,6 +53,14 @@ export interface IJsonLfxWorkspace {
*/
readonly pnpmLockfileFolder: string;

/**
* The path to the `.pnpmfile.cjs` file that is loaded by PNPM. In a Rush workspace,
* this is a temporary file that is generated from `rushPnpmfilePath`.
*
* @example `"common/temp/my-subspace/.pnpmfile.cjs"`
*/
readonly pnpmfilePath: string;

/**
* This section will be defined only if this is a Rush workspace (versus a plain PNPM workspace).
*/
Expand Down
6 changes: 3 additions & 3 deletions apps/lockfile-explorer-web/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ module.exports = function createConfig(env, argv) {
}
},
performance: {
hints: env.production ? 'error' : false
hints: env.production ? 'error' : false,
// This specifies the bundle size limit that will trigger Webpack's warning saying:
// "The following entrypoint(s) combined asset size exceeds the recommended limit."
// maxEntrypointSize: 500000,
// maxAssetSize: 500000
maxEntrypointSize: Infinity,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're setting these to infinity, why not just turn off performance?

Copy link
Collaborator Author

@octogonz octogonz Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iclanton Is there a better way to do that? I tried undefined but in the merge it doesn't undo the rig's setting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's performance: false

maxAssetSize: Infinity
},
devServer: {
port: 8096,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ import cors from 'cors';
import process from 'process';
import open from 'open';
import updateNotifier from 'update-notifier';

import * as path from 'node:path';
import { FileSystem, type IPackageJson, JsonFile, PackageJsonLookup } from '@rushstack/node-core-library';
import { ConsoleTerminalProvider, type ITerminal, Terminal, Colorize } from '@rushstack/terminal';
import {
type CommandLineFlagParameter,
CommandLineParser,
type IRequiredCommandLineStringParameter
} from '@rushstack/ts-command-line';

import {
type LfxGraph,
lfxGraphSerializer,
type IAppContext,
type IJsonLfxGraph
type IJsonLfxGraph,
type IJsonLfxWorkspace
} from '../../../build/lfx-shared';
import * as lockfilePath from '../../graph/lockfilePath';

import type { IAppState } from '../../state';
import { init } from '../../utils/init';
import { PnpmfileRunner } from '../../graph/PnpmfileRunner';
import * as lfxGraphLoader from '../../graph/lfxGraphLoader';

const EXPLORER_TOOL_FILENAME: 'lockfile-explorer' = 'lockfile-explorer';
Expand Down Expand Up @@ -98,6 +102,8 @@ export class ExplorerCommandLineParser extends CommandLineParser {
subspaceName: this._subspaceParameter.value
});

const lfxWorkspace: IJsonLfxWorkspace = appState.lfxWorkspace;

// Important: This must happen after init() reads the current working directory
process.chdir(appState.lockfileExplorerProjectRoot);

Expand Down Expand Up @@ -153,7 +159,7 @@ export class ExplorerCommandLineParser extends CommandLineParser {
const pnpmLockfileText: string = await FileSystem.readFileAsync(appState.pnpmLockfileLocation);
const lockfile: unknown = yaml.load(pnpmLockfileText) as unknown;

const graph: LfxGraph = lfxGraphLoader.generateLockfileGraph(lockfile, appState.lfxWorkspace);
const graph: LfxGraph = lfxGraphLoader.generateLockfileGraph(lockfile, lfxWorkspace);

const jsonGraph: IJsonLfxGraph = lfxGraphSerializer.serializeToJson(graph);
res.type('application/json').send(jsonGraph);
Expand Down Expand Up @@ -183,13 +189,18 @@ export class ExplorerCommandLineParser extends CommandLineParser {
);

app.get('/api/pnpmfile', async (req: express.Request, res: express.Response) => {
const pnpmfilePath: string = lockfilePath.join(
lfxWorkspace.workspaceRootFullPath,
lfxWorkspace.rushConfig?.rushPnpmfilePath ?? lfxWorkspace.pnpmfilePath
);

let pnpmfile: string;
try {
pnpmfile = await FileSystem.readFileAsync(appState.pnpmfileLocation);
pnpmfile = await FileSystem.readFileAsync(pnpmfilePath);
} catch (e) {
if (FileSystem.isNotExistError(e)) {
return res.status(404).send({
message: `Could not load pnpmfile file in this repo.`,
message: `Could not load .pnpmfile.cjs file in this repo: "${pnpmfilePath}"`,
error: `No .pnpmifile.cjs found.`
});
} else {
Expand Down Expand Up @@ -218,10 +229,18 @@ export class ExplorerCommandLineParser extends CommandLineParser {
}
}

const {
hooks: { readPackage }
} = require(appState.pnpmfileLocation);
const parsedPackage: {} = readPackage(packageJson, {});
let parsedPackage: IPackageJson = packageJson;

const pnpmfilePath: string = path.join(lfxWorkspace.workspaceRootFullPath, lfxWorkspace.pnpmfilePath);
if (await FileSystem.existsAsync(pnpmfilePath)) {
const pnpmFileRunner: PnpmfileRunner = new PnpmfileRunner(pnpmfilePath);
try {
parsedPackage = await pnpmFileRunner.transformPackageAsync(packageJson, fileLocation);
} finally {
await pnpmFileRunner.disposeAsync();
}
}

res.send(parsedPackage);
}
);
Expand Down
24 changes: 24 additions & 0 deletions apps/lockfile-explorer/src/graph/IPnpmfileModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import type { IPackageJson } from '@rushstack/node-core-library';

export interface IReadPackageContext {
log: (message: string) => void;
}

export type IReadPackageHook = (
packageJson: IPackageJson,
context: IReadPackageContext
) => IPackageJson | Promise<IPackageJson>;

export interface IPnpmHooks {
readPackage?: IReadPackageHook;
}

/**
* Type of the `.pnpmfile.cjs` module.
*/
export interface IPnpmfileModule {
hooks?: IPnpmHooks;
}
Loading
Loading