Simplify and extract types (#7680)

- prevents anything importing from `app.js`
- generally tidies up and removes unused stuff
- makes typing a bit more correct
This commit is contained in:
Matt Godbolt
2025-05-12 10:28:51 -05:00
committed by GitHub
parent eaa083edc3
commit 59aceae9c4
9 changed files with 118 additions and 61 deletions

30
app.ts
View File

@@ -26,7 +26,6 @@
// see https://docs.sentry.io/platforms/javascript/guides/node/install/late-initialization/
import '@sentry/node/preload'; // preload Sentry's "preload" support before any other imports
////
import child_process from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
@@ -52,6 +51,7 @@ import systemdSocket from 'systemd-socket';
import _ from 'underscore';
import urljoin from 'url-join';
import {AppArguments} from './lib/app.interfaces.js';
import {setBaseDirectory, unwrap} from './lib/assert.js';
import * as aws from './lib/aws.js';
import * as normalizer from './lib/clientstate-normalizer.js';
@@ -71,9 +71,10 @@ import {NoScriptController} from './lib/handlers/api/noscript-controller.js';
import {SiteTemplateController} from './lib/handlers/api/site-template-controller.js';
import {SourceController} from './lib/handlers/api/source-controller.js';
import {CompileHandler} from './lib/handlers/compile.js';
import {ShortLinkMetaData} from './lib/handlers/handler.interfaces.js';
import {cached, createFormDataHandler, csp} from './lib/handlers/middleware.js';
import {NoScriptHandler} from './lib/handlers/noscript.js';
import {RouteAPI, ShortLinkMetaData} from './lib/handlers/route-api.js';
import {RouteAPI} from './lib/handlers/route-api.js';
import {languages as allLanguages} from './lib/languages.js';
import {logToLoki, logToPapertrail, logger, makeLogStream, suppressConsoleLog} from './lib/logger.js';
import {setupMetricsServer} from './lib/metrics-server.js';
@@ -98,7 +99,7 @@ setBaseDirectory(new URL('.', import.meta.url));
);
};
export type CompilerExplorerOptions = Partial<{
type CompilerExplorerOptions = Partial<{
env: string[];
rootDir: string;
host: string;
@@ -221,20 +222,6 @@ const releaseBuildNumber = (() => {
return '';
})();
export type AppDefaultArguments = {
rootDir: string;
env: string[];
hostname?: string;
port: number;
gitReleaseName: string;
releaseBuildNumber: string;
wantedLanguages: string[] | null;
doCache: boolean;
fetchCompilersFromRemote: boolean;
ensureNoCompilerClash: boolean | undefined;
suppressConsoleLog: boolean;
};
function patchUpLanguageArg(languages: string[] | undefined): string[] | null {
if (!languages) return null;
if (languages.length === 1) {
@@ -245,7 +232,7 @@ function patchUpLanguageArg(languages: string[] | undefined): string[] | null {
}
// Set default values for omitted arguments
const defArgs: AppDefaultArguments = {
const defArgs: AppArguments = {
rootDir: opts.rootDir || './etc',
env: opts.env || ['dev'],
hostname: opts.host,
@@ -628,7 +615,6 @@ async function main() {
storageHandler,
compilationEnvironment,
ceProps,
opts,
defArgs,
renderConfig,
renderGoldenLayout,
@@ -704,9 +690,11 @@ async function main() {
const sponsorConfig = loadSponsorsFromString(await fs.readFile(configDir + '/sponsors.yaml', 'utf8'));
function renderConfig(extra: Record<string, any>, urlOptions?: any) {
function renderConfig(extra: Record<string, any>, urlOptions?: Record<string, any>) {
const urlOptionsAllowed = ['readOnly', 'hideEditorToolbars', 'language'];
const filteredUrlOptions = _.mapObject(_.pick(urlOptions, urlOptionsAllowed), val => utils.toProperty(val));
const filteredUrlOptions = _.mapObject(_.pick(urlOptions || {}, urlOptionsAllowed), val =>
utils.toProperty(val),
);
const allExtraOptions = _.extend({}, filteredUrlOptions, extra);
if (allExtraOptions.mobileViewer && allExtraOptions.config) {

37
lib/app.interfaces.ts Normal file
View File

@@ -0,0 +1,37 @@
// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export type AppArguments = {
rootDir: string;
env: string[];
hostname?: string;
port: number;
gitReleaseName: string;
releaseBuildNumber: string;
wantedLanguages: string[] | null;
doCache: boolean;
fetchCompilersFromRemote: boolean;
ensureNoCompilerClash: boolean | undefined;
suppressConsoleLog: boolean;
};

View File

@@ -31,12 +31,12 @@ import fs from 'node:fs';
import _ from 'underscore';
import urljoin from 'url-join';
import {AppDefaultArguments} from '../app.js';
import {basic_comparator, remove} from '../shared/common-utils.js';
import type {CompilerInfo, PreliminaryCompilerInfo} from '../types/compiler.interfaces.js';
import {InstructionSet, InstructionSetsList} from '../types/instructionsets.js';
import type {Language, LanguageKey} from '../types/languages.interfaces.js';
import {Tool, ToolInfo} from '../types/tool.interfaces.js';
import {AppArguments} from './app.interfaces.js';
import {assert, unwrap, unwrapString} from './assert.js';
import {CompileHandler} from './handlers/compile.js';
@@ -54,7 +54,7 @@ const sleep = promisify(setTimeout);
export class CompilerFinder {
compilerProps: CompilerProps['get'];
ceProps: PropertyGetter;
args: AppDefaultArguments;
args: AppArguments;
compileHandler: CompileHandler;
languages: Record<string, Language>;
optionsHandler: ClientOptionsHandler;
@@ -62,7 +62,7 @@ export class CompilerFinder {
constructor(
compileHandler: CompileHandler,
compilerProps: CompilerProps,
args: AppDefaultArguments,
args: AppArguments,
optionsHandler: ClientOptionsHandler,
) {
this.compilerProps = compilerProps.get.bind(compilerProps);

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import {AppArguments} from '../app.interfaces.js';
import {GoldenLayoutRootStruct} from '../clientstate-normalizer.js';
import {CompilationEnvironment} from '../compilation-env.js';
import {ClientOptionsHandler} from '../options-handler.js';
import {PropertyGetter} from '../properties.interfaces.js';
import {StorageBase} from '../storage/index.js';
import {CompileHandler} from './compile.js';
export type RenderConfig = (extra: Record<string, any>, urlOptions?: Record<string, any>) => Record<string, any>;
export type RenderGoldenLayout = (
config: GoldenLayoutRootStruct,
metadata: ShortLinkMetaData,
req: express.Request,
res: express.Response,
) => void;
export type HandlerConfig = {
compileHandler: CompileHandler;
clientOptionsHandler: ClientOptionsHandler;
storageHandler: StorageBase;
ceProps: PropertyGetter;
defArgs: AppArguments;
renderConfig: RenderConfig;
renderGoldenLayout: RenderGoldenLayout;
compilationEnvironment: CompilationEnvironment;
};
export type ShortLinkMetaData = {
ogDescription?: string;
ogAuthor?: string;
ogTitle?: string;
ogCreated?: Date;
};

View File

@@ -33,14 +33,13 @@ import {logger} from '../logger.js';
import {ClientOptionsHandler} from '../options-handler.js';
import {StorageBase} from '../storage/index.js';
import {RenderConfig} from './handler.interfaces.js';
import {cached, csp} from './middleware.js';
function isMobileViewer(req: express.Request) {
return req.header('CloudFront-Is-Mobile-Viewer') === 'true';
}
type RenderConfig = (extra: Record<string, any>, urlOptions?: any) => Record<string, any>;
export class NoScriptHandler {
constructor(
private readonly router: express.Router,

View File

@@ -26,44 +26,21 @@ import zlib from 'node:zlib';
import express from 'express';
import {AppDefaultArguments, CompilerExplorerOptions} from '../../app.js';
import {isString} from '../../shared/common-utils.js';
import {Language} from '../../types/languages.interfaces.js';
import {assert, unwrap} from '../assert.js';
import {ClientStateGoldenifier, ClientStateNormalizer} from '../clientstate-normalizer.js';
import {ClientState} from '../clientstate.js';
import {CompilationEnvironment} from '../compilation-env.js';
import {logger} from '../logger.js';
import {ClientOptionsHandler} from '../options-handler.js';
import {PropertyGetter} from '../properties.interfaces.js';
import {SentryCapture} from '../sentry.js';
import {ExpandedShortLink} from '../storage/base.js';
import {StorageBase} from '../storage/index.js';
import * as utils from '../utils.js';
import {ApiHandler} from './api.js';
import {CompileHandler} from './compile.js';
import {HandlerConfig, ShortLinkMetaData} from './handler.interfaces.js';
import {cached, csp} from './middleware.js';
export type HandlerConfig = {
compileHandler: CompileHandler;
clientOptionsHandler: ClientOptionsHandler;
storageHandler: StorageBase;
ceProps: PropertyGetter;
opts: CompilerExplorerOptions;
defArgs: AppDefaultArguments;
renderConfig: any;
renderGoldenLayout: any;
compilationEnvironment: CompilationEnvironment;
};
export type ShortLinkMetaData = {
ogDescription?: string;
ogAuthor?: string;
ogTitle?: string;
ogCreated?: Date;
};
export class RouteAPI {
renderGoldenLayout: any;
storageHandler: StorageBase;

View File

@@ -30,12 +30,12 @@ import semverParser from 'semver';
import _ from 'underscore';
import urlJoin from 'url-join';
import {AppDefaultArguments} from '../app.js';
import {splitArguments} from '../shared/common-utils.js';
import {CompilerInfo, Remote} from '../types/compiler.interfaces.js';
import type {LanguageKey} from '../types/languages.interfaces.js';
import type {Source} from '../types/source.interfaces.js';
import type {ToolTypeKey} from '../types/tool.interfaces.js';
import {AppArguments} from './app.interfaces.js';
import {logger} from './logger.js';
import type {PropertyGetter, PropertyValue} from './properties.interfaces.js';
@@ -146,7 +146,7 @@ export class ClientOptionsHandler {
* @param {CompilerProps} compilerProps
* @param {Object} defArgs - Compiler Explorer arguments
*/
constructor(fileSources: Source[], compilerProps: CompilerProps, defArgs: AppDefaultArguments) {
constructor(fileSources: Source[], compilerProps: CompilerProps, defArgs: AppArguments) {
this.compilerProps = compilerProps.get.bind(compilerProps);
this.ceProps = compilerProps.ceProps;
const ceProps = compilerProps.ceProps;

View File

@@ -29,12 +29,8 @@ import {beforeAll, describe, expect, it} from 'vitest';
import express from 'express';
import request from 'supertest';
import {GoldenLayoutRootStruct} from '../../lib/clientstate-normalizer.js';
import {
HandlerConfig,
RouteAPI,
ShortLinkMetaData,
extractJsonFromBufferAndInflateIfRequired,
} from '../../lib/handlers/route-api.js';
import {HandlerConfig, ShortLinkMetaData} from '../../lib/handlers/handler.interfaces.js';
import {RouteAPI, extractJsonFromBufferAndInflateIfRequired} from '../../lib/handlers/route-api.js';
function possibleCompression(buffer: Buffer): boolean {
// code used in extractJsonFromBufferAndInflateIfRequired

View File

@@ -27,7 +27,7 @@ import {fileURLToPath} from 'node:url';
import _ from 'underscore';
import {beforeAll, describe, expect, it} from 'vitest';
import {AppDefaultArguments} from '../app.js';
import {AppArguments} from '../lib/app.interfaces.js';
import {BaseCompiler} from '../lib/base-compiler.js';
import {CompilationEnvironment} from '../lib/compilation-env.js';
import {CompilerFinder} from '../lib/compiler-finder.js';
@@ -170,13 +170,13 @@ describe('Options handler', () => {
beforeAll(() => {
fakeOptionProps = properties.fakeProps(optionsProps);
compilerProps = new properties.CompilerProps(languages, fakeOptionProps);
optionsHandler = new ClientOptionsHandler([], compilerProps, {env: ['dev']} as unknown as AppDefaultArguments);
optionsHandler = new ClientOptionsHandler([], compilerProps, {env: ['dev']} as unknown as AppArguments);
fakeMoreCompilerProps = properties.fakeProps(moreLibProps);
moreCompilerProps = new properties.CompilerProps(languages, fakeMoreCompilerProps);
moreOptionsHandler = new ClientOptionsHandler([], moreCompilerProps, {
env: ['dev'],
} as unknown as AppDefaultArguments);
} as unknown as AppArguments);
env = {
ceProps: properties.fakeProps({}),