Refactor config json into a virtual module

This commit is contained in:
Sebastian Martinez
2024-05-17 15:35:00 +02:00
parent 13a6925bcf
commit 46e514dcd8
29 changed files with 140 additions and 183 deletions
+1
View File
@@ -1,6 +1,7 @@
/build/
node_modules/
NOTES
config/local*
# KaTeX files
*.min.css
+24
View File
@@ -0,0 +1,24 @@
{
"nodes": {
"fallbackPublicExplorer": "FALLBACK_PUBLIC_EXPLORER",
"apiVersion": "API_VERSION",
"defaultHttpdPort": "DEFAULT_HTTPD_PORT",
"defaultLocalHttpdPort": "DEFAULT_LOCAL_HTTPD_PORT",
"defaultHttpdHostname": "DEFAULT_HTTPD_HOSTNAME",
"defaultHttpdScheme": "DEFAULT_HTTPD_SCHEME",
"defaultNodePort": "DEFAULT_NODE_PORT",
"pinned": {
"__name": "PINNED_NODES",
"__format": "json"
}
},
"supportWebsite": "SUPPORT_WEBSITE",
"reactions": {
"__name": "REACTIONS",
"__format": "json"
},
"fallbackPreferredSeed": {
"__name": "FALLBACK_PREFERRED_SEED",
"__format": "json"
}
}
+1
View File
@@ -0,0 +1 @@
default.json
+17
View File
@@ -0,0 +1,17 @@
{
"nodes": {
"defaultHttpdPort": 8081,
"defaultLocalHttpdPort": 8081,
"defaultHttpdHostname": "127.0.0.1",
"defaultHttpdScheme": "http",
"pinned": [
{
"baseUrl": {
"hostname": "127.0.0.1",
"port": 8081,
"scheme": "http"
}
}
]
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
import type { ZodIssue, ZodType, TypeOf } from "zod";
import { config } from "@app/lib/config";
import config from "virtual:config";
import { compare } from "compare-versions";
export interface BaseUrl {
+8 -1
View File
@@ -1,7 +1,14 @@
import { defineConfig } from "vite";
import nodeConfig from "config";
import path from "node:path";
import virtual from "vite-plugin-virtual";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
virtual({
"virtual:config": nodeConfig.util.toObject(),
}),
],
test: {
environment: "happy-dom",
include: ["httpd-client/tests/*.test.ts"],
Vendored
+19
View File
@@ -0,0 +1,19 @@
declare module "virtual:*" {
const config: {
nodes: {
apiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultHttpdHostname: string;
defaultLocalHttpdPort: number;
defaultNodePort: number;
defaultHttpdScheme: string;
pinned: { baseUrl: BaseUrl }[];
};
reactions: string[];
supportWebsite: string;
fallbackPreferredSeed: BaseUrl;
};
export default config;
}
+42
View File
@@ -33,6 +33,7 @@
"@playwright/test": "^1.42.1",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tsconfig/svelte": "^5.0.3",
"@types/config": "^3.3.4",
"@types/dompurify": "^3.0.5",
"@types/katex": "^0.16.7",
"@types/lodash": "^4.17.0",
@@ -42,6 +43,7 @@
"@types/wait-on": "^5.3.4",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"chalk": "^5.3.0",
"config": "^3.3.11",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-no-only-tests": "^3.1.0",
@@ -55,6 +57,7 @@
"svelte-check": "^3.6.8",
"typescript": "^5.4.3",
"vite": "^5.2.6",
"vite-plugin-virtual": "^0.3.0",
"vitest": "^1.4.0",
"wait-on": "^7.2.0"
},
@@ -1020,6 +1023,12 @@
"integrity": "sha512-Ms0t9K0oxioSb0lrZ5NRysx0nE/KsojYOG+db9v6wSaU/+P37vc0WRmh1QE1c8IAtTniD4yEhffGQuTKF8uaPw==",
"dev": true
},
"node_modules/@types/config": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@types/config/-/config-3.3.4.tgz",
"integrity": "sha512-qFiTLnWy+TdPSMIXFHP+87lFXFRM4SXjRS+CSB66+56TrpLNw003y1sh7DGaaC1NGesxgKoT5FDy6dyA1Xju/g==",
"dev": true
},
"node_modules/@types/dompurify": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
@@ -1894,6 +1903,18 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/config": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/config/-/config-3.3.11.tgz",
"integrity": "sha512-Dhn63ZoWCW5EMg4P0Sl/XNsj/7RLiUIA1x1npCy+m2cRwRHzLnt3UtYtxRDMZW/6oOMdWhCzaGYkOcajGgrAOA==",
"dev": true,
"dependencies": {
"json5": "^2.2.3"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3159,6 +3180,18 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/jsonc-parser": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
@@ -5017,6 +5050,15 @@
"url": "https://opencollective.com/vitest"
}
},
"node_modules/vite-plugin-virtual": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/vite-plugin-virtual/-/vite-plugin-virtual-0.3.0.tgz",
"integrity": "sha512-TOtrWw6jKrJNXfxhGRUiQzfAP1gRkYkVzMkJNjHUJ8idLuxf8eeeDKZKZHhdeYfaCc/87rv+KvWE2iCy1QInWA==",
"dev": true,
"peerDependencies": {
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
}
},
"node_modules/vite/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+5 -2
View File
@@ -8,8 +8,8 @@
"check": "scripts/check",
"format": "npx prettier '**/*.@(ts|js|svelte|json|css|html|yml)' --write",
"test:unit": "TZ='UTC' vitest run",
"test:e2e": "TZ='UTC' playwright test",
"test:httpd-api:unit": "TZ='UTC' vitest run --config httpd-client/vite.config.ts --reporter verbose"
"test:e2e": "NODE_CONFIG_ENV='test' TZ='UTC' playwright test",
"test:httpd-api:unit": "NODE_CONFIG_ENV='test' TZ='UTC' vitest run --config httpd-client/vite.config.ts --reporter verbose"
},
"type": "module",
"engines": {
@@ -19,6 +19,7 @@
"@playwright/test": "^1.42.1",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tsconfig/svelte": "^5.0.3",
"@types/config": "^3.3.4",
"@types/dompurify": "^3.0.5",
"@types/katex": "^0.16.7",
"@types/lodash": "^4.17.0",
@@ -28,6 +29,7 @@
"@types/wait-on": "^5.3.4",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"chalk": "^5.3.0",
"config": "^3.3.11",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-no-only-tests": "^3.1.0",
@@ -41,6 +43,7 @@
"svelte-check": "^3.6.8",
"typescript": "^5.4.3",
"vite": "^5.2.6",
"vite-plugin-virtual": "^0.3.0",
"vitest": "^1.4.0",
"wait-on": "^7.2.0"
},
+1 -1
View File
@@ -31,7 +31,7 @@
void httpd.initialize().finally(() => void router.loadFromLocation());
if (!window.VITEST && !window.PLAYWRIGHT && import.meta.env.PROD) {
if (import.meta.env.PROD) {
const plausible = Plausible({ domain: "app.radicle.xyz" });
plausible.enableAutoPageviews();
+1 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { api, changeHttpdPort } from "@app/lib/httpd";
import { config } from "@app/lib/config";
import config from "virtual:config";
import {
codeFont,
codeFonts,
+1 -1
View File
@@ -2,7 +2,7 @@
import type { ComponentProps } from "svelte";
import type { ErrorParam } from "@app/lib/router/definitions";
import { config } from "@app/lib/config";
import config from "virtual:config";
import Command from "./Command.svelte";
import ExternalLink from "./ExternalLink.svelte";
import Icon from "./Icon.svelte";
+1 -1
View File
@@ -3,7 +3,7 @@
import { createEventDispatcher } from "svelte";
import config from "@app/config.json";
import config from "virtual:config";
import IconButton from "./IconButton.svelte";
import IconSmall from "./IconSmall.svelte";
-25
View File
@@ -1,25 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import type { Config } from "@app/lib/config";
import type { FakeTimers } from "@sinonjs/fake-timers";
declare global {
interface Window {
// Defined in vite.config.ts and are available in all environments except
// production.
VITEST: boolean;
PLAYWRIGHT: boolean;
// APP_CONFIG is set from within Playwright tests at runtime.
// To better understand how it works together, have a look at:
// tests/support/fixtures.ts
// src/config.ts
APP_CONFIG: Config;
// eslint-disable-next-line @typescript-eslint/ban-types
initializeTestStubs?: Function;
e2eTestStubs: {
FakeTimers: FakeTimers;
};
}
}
export {};
-48
View File
@@ -1,48 +0,0 @@
import type { BaseUrl } from "@httpd-client";
import configJson from "@app/config.json";
export interface Config {
nodes: {
apiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultHttpdHostname: string;
defaultLocalHttpdPort: number;
defaultNodePort: number;
defaultHttpdScheme: string;
pinned: { baseUrl: BaseUrl }[];
};
supportWebsite: string;
fallbackPreferredSeed: BaseUrl;
}
function getConfig(): Config {
if (window.VITEST) {
return {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdHostname: "127.0.0.1",
defaultHttpdPort: 8081,
defaultLocalHttpdPort: 8081,
defaultHttpdScheme: "http",
defaultNodePort: 8776,
pinned: [],
},
supportWebsite: "https://radicle.zulipchat.com",
fallbackPreferredSeed: {
hostname: "seed.radicle.garden",
port: 443,
scheme: "https",
},
};
} else if (window.PLAYWRIGHT) {
return window.APP_CONFIG;
} else {
// In dev and production environments we use data from config.json.
return configJson;
}
}
export const config = getConfig();
+1 -1
View File
@@ -4,7 +4,7 @@ import { get, writable } from "svelte/store";
import { withTimeout, Mutex, E_CANCELED, E_TIMEOUT } from "async-mutex";
import { HttpdClient } from "@httpd-client";
import { config } from "@app/lib/config";
import config from "virtual:config";
import { deduplicateStore } from "@app/lib/deduplicateStore";
import { experimental } from "./appearance";
+1 -1
View File
@@ -5,7 +5,7 @@ import { get, writable } from "svelte/store";
import * as mutexExecutor from "@app/lib/mutexExecutor";
import * as utils from "@app/lib/utils";
import { config } from "@app/lib/config";
import config from "virtual:config";
import {
projectRouteToPath,
projectTitle,
+1 -1
View File
@@ -5,7 +5,7 @@ import { writable, derived, get } from "svelte/store";
import { number, string, object } from "zod";
import { api, httpdStore, type HttpdState } from "./httpd";
import { config } from "./config";
import config from "virtual:config";
const preferredSeedSchema = object({
hostname: string(),
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { config } from "@app/lib/config";
import config from "virtual:config";
import Command from "@app/components/Command.svelte";
import ExternalLink from "@app/components/ExternalLink.svelte";
+1 -1
View File
@@ -2,7 +2,7 @@ import type { BaseUrl, NodeStats, Policy, Scope } from "@httpd-client";
import type { ErrorRoute, NotFoundRoute } from "@app/lib/router/definitions";
import { HttpdClient } from "@httpd-client";
import { config } from "@app/lib/config";
import config from "virtual:config";
import { baseUrlToString } from "@app/lib/utils";
import { handleError } from "@app/views/nodes/error";
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import type { BaseUrl } from "@httpd-client";
import { config } from "@app/lib/config";
import config from "virtual:config";
import { parseRepositoryId } from "@app/lib/utils";
import Button from "@app/components/Button.svelte";
+1 -1
View File
@@ -5,7 +5,7 @@
import { activeUnloadedRouteStore, routeToPath } from "@app/lib/router";
import { api } from "@app/lib/httpd";
import { config } from "@app/lib/config";
import config from "virtual:config";
import { formatPublicExplorer } from "@app/lib/utils";
import { queryProject } from "@app/lib/projects";
+1 -1
View File
@@ -4,7 +4,7 @@
import debounce from "lodash/debounce";
import { api, httpdStore } from "@app/lib/httpd";
import { isLocal, toClipboard } from "@app/lib/utils";
import { config } from "@app/lib/config";
import config from "virtual:config";
import Button from "@app/components/Button.svelte";
import IconSmall from "@app/components/IconSmall.svelte";
+1 -6
View File
@@ -1,12 +1,7 @@
import { appConfigWithFixture, expect, test } from "@tests/support/fixtures.js";
test.use({
customAppConfig: true,
});
import { expect, test } from "@tests/support/fixtures.js";
test("show pinned projects", async ({ page }) => {
await page.addInitScript(() => localStorage.setItem("experimental", "true"));
await page.addInitScript(appConfigWithFixture);
await page.goto("/");
await expect(page.getByText("Local projects")).toBeVisible();
-3
View File
@@ -1,7 +1,6 @@
import {
aliceMainHead,
aliceRemote,
appConfigWithFixture,
expect,
sourceBrowsingUrl,
test,
@@ -13,8 +12,6 @@ import {
} from "@tests/support/router.js";
test("navigate between landing and project page", async ({ page }) => {
await page.addInitScript(appConfigWithFixture);
await page.goto("/");
await expect(page).toHaveURL("/");
+1 -68
View File
@@ -24,16 +24,13 @@ const fixturesDir = Path.resolve(supportDir, "..", "./fixtures");
export const test = base.extend<{
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
forAllTests: void;
customAppConfig: boolean;
stateDir: string;
peerManager: PeerManager;
authenticatedPeer: RadiclePeer;
outputLog: Stream.Writable;
}>({
customAppConfig: [false, { option: true }],
forAllTests: [
async ({ customAppConfig, outputLog, page }, use) => {
async ({ outputLog, page }, use) => {
const browserLabel = logLabel.logPrefix("browser");
let sinonPath = fileURLToPath(import.meta.resolve("sinon"));
// The exports in sinon-esm.js mess up our test pipeline
@@ -78,41 +75,6 @@ export const test = base.extend<{
});
}
if (!customAppConfig) {
// Remember: `page.addInitScript()` is run in the browser which
// is completely isolated from the test environment, so we don't have
// access to any variables that we have in the test.
await page.addInitScript(() => {
window.APP_CONFIG = {
nodes: {
fallbackPublicExplorer:
"https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort: 8081,
defaultHttpdScheme: "http",
defaultNodePort: 8776,
pinned: [
{
baseUrl: {
hostname: "127.0.0.1",
port: 8081,
scheme: "http",
},
},
],
},
supportWebsite: "https://radicle.zulipchat.com",
fallbackPreferredSeed: {
hostname: "seed.radicle.garden",
port: 443,
scheme: "https",
},
};
});
}
const playwrightLabel = logLabel.logPrefix("playwright");
function isLocalhost(url: URL) {
@@ -221,35 +183,6 @@ function log(text: string, label: string, outputLog: Stream.Writable) {
}
}
export function appConfigWithFixture(defaultLocalHttpdPort = 8081) {
window.APP_CONFIG = {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort,
defaultHttpdScheme: "http",
defaultNodePort: 8776,
pinned: [
{
baseUrl: {
hostname: "127.0.0.1",
port: 8081,
scheme: "http",
},
},
],
},
supportWebsite: "https://radicle.zulipchat.com",
fallbackPreferredSeed: {
hostname: "seed.radicle.garden",
port: 443,
scheme: "https",
},
};
}
export async function createSourceBrowsingFixture(
peerManager: PeerManager,
palm: RadiclePeer,
+3 -12
View File
@@ -1,8 +1,4 @@
import { test, expect, appConfigWithFixture } from "@tests/support/fixtures.js";
test.use({
customAppConfig: true,
});
import { test, expect } from "@tests/support/fixtures.js";
import sinon from "sinon";
test("pinned projects", async ({ page }) => {
@@ -14,12 +10,11 @@ test("pinned projects", async ({ page }) => {
});
});
await page.addInitScript(appConfigWithFixture);
await page.goto("/", { waitUntil: "networkidle" });
await expect(page).toHaveScreenshot();
});
test("load error", async ({ page }) => {
test("load projects error", async ({ page }) => {
await page.addInitScript(() => {
sinon.useFakeTimers({
now: new Date("November 24 2022 12:00:00").valueOf(),
@@ -29,18 +24,15 @@ test("load error", async ({ page }) => {
});
await page.route(
({ pathname }) =>
pathname === "/api/v1/projects/rad:z4BwwjPCFNVP27FwVbDFgwVwkjcir",
({ pathname }) => pathname === "/api/v1/projects",
route => route.fulfill({ status: 500 }),
);
await page.addInitScript(appConfigWithFixture, 8090);
await page.goto("/", { waitUntil: "networkidle" });
await expect(page).toHaveScreenshot();
});
test("response parse error", async ({ page }) => {
await page.addInitScript(appConfigWithFixture);
await page.route("*/**/v1/projects*", route => {
return route.fulfill({
json: [{ name: 1337 }],
@@ -51,7 +43,6 @@ test("response parse error", async ({ page }) => {
});
test("response error", async ({ page }) => {
await page.addInitScript(appConfigWithFixture);
await page.route("*/**/v1/projects*", route => {
return route.fulfill({
status: 500,
+5 -5
View File
@@ -1,4 +1,6 @@
import config from "config";
import path from "node:path";
import virtual from "vite-plugin-virtual";
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
@@ -9,6 +11,9 @@ export default defineConfig({
reporters: "verbose",
},
plugins: [
virtual({
"virtual:config": config.util.toObject(),
}),
svelte({
// Reference: https://github.com/sveltejs/vite-plugin-svelte/issues/270#issuecomment-1033190138
dynamicCompileOptions({ filename }) {
@@ -56,9 +61,4 @@ export default defineConfig({
},
},
},
define: {
VITEST: process.env.VITEST !== undefined,
PLAYWRIGHT: process.env.PLAYWRIGHT_TEST_BASE_URL !== undefined,
},
});