sourcegraph/client/web-sveltekit
Jason Hawk Harris cff1669bc1
Svelte: Perforce UI elements refactor (#64279)
## UI Updates for Perforce Depots and Git Repos

Fixes SRCH-530

**NOTE: This PR is a refactor of an earlier
[PR](https://github.com/sourcegraph/sourcegraph/pull/64014) that was
reverted. For that reason, the PR description is largely the same.**

This PR introduces changes to the UI to differentiate between Perforce
Depots and Git repositories. Below are the key changes included in this
commit:

### 1. Dynamic Top-Level Navigation

**For Perforce Depots:**

![Screenshot 2024-07-31 at 10 10 37
AM](https://github.com/user-attachments/assets/2d261b51-f8fa-4599-acae-3520d38996f3)

**For Git Repos:**

![Screenshot 2024-07-31 at 10 10 14
AM](https://github.com/user-attachments/assets/0f9ee3f7-918a-42d8-908f-04593ed52ebd)

### 2. Tabs on Revision Picker

**For Perforce Depots:**

Since we only need one tab for changelists, no tabs are shown.

![Screenshot 2024-07-31 at 10 20 24
AM](https://github.com/user-attachments/assets/f1006d56-67aa-41ab-a13b-905e157cb283)

**For Git Repos:**

We have tabs for Branches, Tags, and Commits.

![Screenshot 2024-07-31 at 10 23 02
AM](https://github.com/user-attachments/assets/38907d51-0407-4cd7-ad4c-1c5967dfddf3)

### 3. Commits/Changelists Page

**For Git Repos:**

The page displays Git commits.

![Screenshot 2024-07-31 at 10 26 23
AM](https://github.com/user-attachments/assets/85245d1d-708f-4d51-9da3-0425c3f085d0)

**For Perforce Depots:**

The page displays Perforce changelists.

![Screenshot 2024-07-31 at 10 26 39
AM](https://github.com/user-attachments/assets/2f6f16aa-d498-4763-949d-d1a13f9a26ac)

### 4. Vocabulary Adjustments

- We display either Git commit SHAs or Changelist IDs based on the
project type.
- For authorship, we use "submitted by" for Perforce and "committed by"
for Git.
- We refer to "Commits" for Git projects and "Changelists" for Perforce
projects.

**Examples:**

- **For Git Commits:**

![Screenshot 2024-07-31 at 10 37 08
AM](https://github.com/user-attachments/assets/ac15b0b3-4c85-4a4c-80c0-ec9384b72eca)

- **For Perforce Changelists:**

![Screenshot 2024-07-31 at 10 37 35
AM](https://github.com/user-attachments/assets/4230cb32-5285-4141-b374-f3ea23042e1d)

### 5. URL Mapping

URLs are now structured differently based on the project type:

- **Commits Page:**
  - Git: `/[repo-name]/-/commits`
  - Perforce: `/[repo-name]/-/changelists`
  
- **Individual Item Page:**
  - Git: `/[repo-name]/-/commit/[commit-hash]`
  - Perforce: `/[depot-name]/-/changelist/[changelist-ID]`

When viewing a specific commit or changelist:
- **Git:** `/[repo-name]@[git-commit-hash]`
- **Perforce:** `/[repo-name]@changelist/[changelist-id]`

_NOTE: The value displayed in the search field will also change
accordingly._


### What is left to be done?
**On repo search results, when searching a revision, we still show the
git commit SHA instead of the changelist ID for perforce depots:**
![Screenshot 2024-07-31 at 10 59 12
AM](https://github.com/user-attachments/assets/38bc2a3e-be8b-4585-9fe0-776149a7f230)

I plan to make a follow-up issue for this and begin work on it
immediately. It's a little trickier than the other changes because in
the RepositoryMatch type, there is no value that can help us determine
whether a project is a depot or a repo. We need to find another way to
fetch that data.

### Request for reviewers: 
1. Please try to break these new features and tell me what you find. I
stumbled on a number of little gotchas while working on this, and I'm
sure I've missed some.

## Test plan
<!-- REQUIRED; info at
https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles
-->
- Manual/Visual testing
- Adjust e2e and integration tests to obtain a passing CI
- Test directly visiting a URL versus getting there via click
- Add unit tests for new/updated helper functions

---------

Co-authored-by: Camden Cheek <camden@ccheek.com>
2024-08-14 19:18:24 +00:00
..
.storybook Svelte: implement new reference panel against new Usages API (#63724) 2024-07-18 13:32:45 -06:00
assets Svelte: implement new reference panel against new Usages API (#63724) 2024-07-18 13:32:45 -06:00
dev refactor(svelte): Improve integration with sg (#64333) 2024-08-08 11:39:26 +02:00
src Svelte: Perforce UI elements refactor (#64279) 2024-08-14 19:18:24 +00:00
static svelte: Update messaging and icon for switching to/from the prototype (#60049) 2024-02-02 15:07:40 +01:00
.eslintignore svelte: Add support for integration tests and GraphQL mocking (#59585) 2024-01-15 21:38:58 +01:00
.eslintrc.cjs svelte: Refactor search results page (#59029) 2023-12-15 18:11:28 +01:00
.gitignore svelte: Add support for integration tests and GraphQL mocking (#59585) 2024-01-15 21:38:58 +01:00
.graphqlrc svelte: Towards a better data fetching and GraphQL authoring experience (#59383) 2024-01-09 10:33:30 +01:00
.npmrc [experiment] Merge SvelteKit prototype into main (#47238) 2023-02-13 17:53:23 +01:00
.prettierignore svelte: Add lucide icons and new icon API (#62908) 2024-05-29 11:25:04 +02:00
.stylelintrc.json Svelte: File view visual updates (#62126) 2024-04-25 11:08:46 +01:00
build-and-push.sh [experiment] Merge SvelteKit prototype into main (#47238) 2023-02-13 17:53:23 +01:00
build-docker.sh [experiment] Merge SvelteKit prototype into main (#47238) 2023-02-13 17:53:23 +01:00
BUILD.bazel feat(svelte): Migrate dotcom community search pages to Svelte (#64388) 2024-08-09 22:52:47 +02:00
Dockerfile [experiment] Merge SvelteKit prototype into main (#47238) 2023-02-13 17:53:23 +01:00
gulpfile.cjs Enable bazel for web-sveltekit and make it available in production builds (#55177) 2023-10-16 14:15:59 +02:00
package.json chore(web): Upgrade playwright (#64384) 2024-08-09 13:01:46 +00:00
playwright.config.ts refactor(svelte): Improve integration with sg (#64333) 2024-08-08 11:39:26 +02:00
prettier.config.cjs chore(svelte): Refactor infinity query implementation (#63824) 2024-07-16 06:51:36 +02:00
README.md build(svelte): Build Svelte app with enterprise runset (#64355) 2024-08-08 16:18:29 +02:00
server.js Better import ordering with prettier (#48188) 2023-03-13 08:37:23 +00:00
svelte.config.js build(svelte): Build Svelte app with enterprise runset (#64355) 2024-08-08 16:18:29 +02:00
tsconfig.json sveltekit: Add support for feature flags (and improve testing) (#55474) 2023-08-01 13:44:22 +02:00
utils.bzl feat(ci): Adds playwright tests for sveltekit to bazel (#62560) 2024-06-06 12:45:05 -06:00
vite.config.ts refactor(svelte): Improve integration with sg (#64333) 2024-08-08 11:39:26 +02:00

Sourcegraph SvelteKit

This folder contains the experimental SvelteKit implementation of the Sourcegraph app.

Developing

There are multiple ways to start the app:

  1. Standalone and proxying to S2
cd client/web-sveltekit
pnpm dev

Then go to (usually) http://localhost:5173.

Or via sg:

sg start web-sveltekit-standalone

Then go to https://sourcegraph.test:5173.

  1. Standalone and proxying to dotcom
cd client/web-sveltekit
pnpm dev:dotcom
  1. Standalone and proxying to another Sourcegraph instance
cd client/web-sveltekit
SOURCEGRAPH_API_URL=https://<instance> pnpm dev

Then go to (usually) http://localhost:5173.

  1. Against a local Sourcegraph instance
sg start enterprise-sveltekit

Then go to https://sourcegraph.test:5173.

Using code from @sourcegraph/*

There are some things to consider when using code from other @sourcegraph packages:

  • Since we use the barrel style of organizing our modules, many (unused) dependencies are imported into the app. This isn't ideal and at best will only increase the initial loading time. Some modules, especially those that access browser specific features during module initialization, can even cause the dev build to fail.
  • Reusing code is great, but also potentially exposes someone who modifies the reused code to this package and therefore Svelte (if the reused code changes in an incompatible way, this package needs to be updated too). To limit the exposure, a module of any @sourcegraph/* package should only be imported once into this package and only into a TypeScript file. The current convention is to import any modules from @sourcegraph/common into src/lib/common.ts, etc.

Tests

We use vitest for unit tests and playwright for integration tests. Both of these are located next to the source files they test. Vitest files end with .test.ts and Playwright files end with .spec.ts.

For example the Playwright test for testing src/routes/search/+page.svelte is located at src/routes/search/page.spec.ts.

Locally you can run the tests with

pnpm vitest # Run vitest tests
pnpm test # Run playwright tests

You can also run playwright tests against a running vite dev server. This is useful for debugging tests.

# In one terminal
pnpm dev
# In another terminal
pnpm test:dev

Both vitest and playwright tests are run in CI.

Formatting and linting

This package defines its own rules for formatting (which includes support for Svelte components) and linting. The workspace rules linting and formatting commands have not been updated yet to keep this experiment contained.

Run

pnpm run lint
pnpm run format

inside this directory.

There is also the pnpm run check command which uses svelte-check to validate TypeScript, CSS, etc in Svelte components. This currently produces many errors because it also validates imported modules from other packages, and we are not explicitly marking type-only imports with type in other parts of the code base (which is required by this package). This noise can be avoided by running the corresponding bazel command instead:

bazel test //client/web-sveltekit:svelte-check

Icons

We use unplugin-icons together with unplugin-auto-import to manage icons. This allows us to use icons from multiple icon sets without having to import them manually.

For a list of currently available icon sets see the @iconify-json/* packages in the package.json file.

Icon references have the form I<IconSetName><IconName>. For example the corner down left arrow from Lucide can be referenced as ILucideCornerDownLeft.

The icon reference is then used in the Icon component. Note that the icon doesn't have to be imported manually.

<script lang="ts">
  import { Icon } from '$lib/Icon.svelte';
</script>

<Icon icon={ILucideCornerDownLeft} />

When the development server is running, the icon will be automatically added to auto-imports.d.ts so TypeScript knows about it.

Data loading with GraphQL

This project makes use of query composition, i.e. components define their own data dependencies via fragments, which get composed by their callers and are eventually being used in a query in a loader.

This goal of this approach is to make data dependencies co-located and easier to change, as well to make the flow of data clearer. Data fetching should only happen in data loaders, not components.

There are a couple of issues to consider with this approach and sometimes we'll have to make exceptions:

  • Caching: If every loader composes its own query it's possible that two queries fetch the same data, in which case we miss out on caching. If caching the data is more important than data co-location it might be preferable to define a reusable query function. Example: File list for currently opened folder (sidebar + folder page)
  • Shared data from layout loaders: While it's very convenient that pages have access to any data from the ancestor layout loaders, that doesn't work well with data dependency co-location. The layout loaders don't know which sub-layout or sub-page is loaded and what data it needs. Fortunately we don't have a lot of data (yet) that is used this way. The prime example for this right now is information about the authenticated user. The current approach is to name data-dependencies on the current user as <ComponentName>_AuthenticatedUser and use that fragment in the AuthenticatedUser fragment in src/routes/layout.gql. This approach might change as we uncover more use cases.
  • On demand data loading: Not all data is fetched/needed immediately for rendering page. Data for e.g. typeaheads is fetched on demand. Ideally the related queries are still composed by the data loader, which passes a function for fetching the data to the page.

Rolling out pages to production

For a page to be accessible in production, the server needs to know to serve the SvelteKit for that page. Due to file based routing we can easily determine available pages during build time. The list of available pages is generated by the sg generate command, which in turn runs bazel run //client/web-sveltekit:write_generated.

To enable a page in production by default, add the following comment to the +page.svelte file:

<script lang="ts">
   // @sg EnableRollout
   ...
</script>

and run sg generate or bazel run //client/web-sveltekit:write_generated to update the list of available pages.

Production build

A production version of this app can be built with

pnpm run build

Currently SvelteKit is configured to create a client-side single page application.