mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 07:04:04 -05:00
Tsify app.js (#4767)
I forgot about app.js, now this is actually the last file in our ts conversion (other than test/)
This commit is contained in:
2
.idea/runConfigurations/app_js.xml
generated
2
.idea/runConfigurations/app_js.xml
generated
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="app.js" type="NodeJSConfigurationType" application-parameters="--language c++" nameIsGenerated="true" node-parameters="-r esm -r ts-node/register" path-to-js-file="app.js" working-dir="$PROJECT_DIR$">
|
||||
<configuration default="false" name="app.ts" type="NodeJSConfigurationType" application-parameters="--language c++" nameIsGenerated="true" node-parameters="-r esm -r ts-node/register" path-to-js-file="app.ts" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
2
.idea/scopes/Server.xml
generated
2
.idea/scopes/Server.xml
generated
@@ -1,3 +1,3 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="Server" pattern="file[compiler-explorer]:lib//*||file[compiler-explorer]:app.js||file[compiler-explorer]:test//*" />
|
||||
<scope name="Server" pattern="file[compiler-explorer]:lib//*||file[compiler-explorer]:app.ts||file[compiler-explorer]:test//*" />
|
||||
</component>
|
||||
6
Makefile
6
Makefile
@@ -86,15 +86,15 @@ run: ## Runs the site like it runs in production
|
||||
|
||||
.PHONY: dev
|
||||
dev: prereqs ## Runs the site as a developer; including live reload support and installation of git hooks
|
||||
./node_modules/.bin/supervisor -w app.js,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.js $(EXTRA_ARGS)
|
||||
./node_modules/.bin/supervisor -w app.ts,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.ts $(EXTRA_ARGS)
|
||||
|
||||
.PHONY: gpu-dev
|
||||
gpu-dev: prereqs ## Runs the site as a developer; including live reload support and installation of git hooks
|
||||
./node_modules/.bin/supervisor -w app.js,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.js --env gpu $(EXTRA_ARGS)
|
||||
./node_modules/.bin/supervisor -w app.ts,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.ts --env gpu $(EXTRA_ARGS)
|
||||
|
||||
.PHONY: debug
|
||||
debug: prereqs ## Runs the site as a developer with full debugging; including live reload support and installation of git hooks
|
||||
./node_modules/.bin/supervisor -w app.js,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --inspect 9229 --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.js --debug $(EXTRA_ARGS)
|
||||
./node_modules/.bin/supervisor -w app.ts,lib,etc/config,static/tsconfig.json -e 'js|ts|node|properties|yaml' -n exit --inspect 9229 --exec $(NODE) $(NODE_ARGS) -- -r esm -r ts-node/register ./app.ts --debug $(EXTRA_ARGS)
|
||||
|
||||
.PHONY:
|
||||
asm-docs:
|
||||
|
||||
@@ -45,6 +45,7 @@ import urljoin from 'url-join';
|
||||
|
||||
import * as aws from './lib/aws';
|
||||
import * as normalizer from './lib/clientstate-normalizer';
|
||||
import {ElementType} from './lib/common-utils';
|
||||
import {CompilationEnvironment} from './lib/compilation-env';
|
||||
import {CompilationQueue} from './lib/compilation-queue';
|
||||
import {CompilerFinder} from './lib/compiler-finder';
|
||||
@@ -66,6 +67,7 @@ import {sources} from './lib/sources';
|
||||
import {loadSponsorsFromString} from './lib/sponsors';
|
||||
import {getStorageTypeByKey} from './lib/storage';
|
||||
import * as utils from './lib/utils';
|
||||
import {Language, LanguageKey} from './types/languages.interfaces';
|
||||
|
||||
// Used by assert.ts
|
||||
global.ce_base_directory = __dirname; // eslint-disable-line unicorn/prefer-module
|
||||
@@ -106,7 +108,8 @@ if (opts.debug) logger.level = 'debug';
|
||||
// AP: Detect if we're running under Windows Subsystem for Linux. Temporary modification
|
||||
// of process.env is allowed: https://nodejs.org/api/process.html#process_process_env
|
||||
if (process.platform === 'linux' && child_process.execSync('uname -a').toString().includes('Microsoft')) {
|
||||
process.env.wsl = true;
|
||||
// Node wants process.env is essentially a Record<key, string | undefined>. Any non-empty string should be fine.
|
||||
process.env.wsl = 'true';
|
||||
}
|
||||
|
||||
// AP: Allow setting of tmpDir (used in lib/base-compiler.js & lib/exec.js) through opts.
|
||||
@@ -214,22 +217,24 @@ props.initialize(configDir, propHierarchy);
|
||||
const ceProps = props.propsFor('compiler-explorer');
|
||||
defArgs.wantedLanguages = ceProps('restrictToLanguages', defArgs.wantedLanguages);
|
||||
|
||||
let languages = allLanguages;
|
||||
if (defArgs.wantedLanguages) {
|
||||
const filteredLangs = {};
|
||||
const passedLangs = defArgs.wantedLanguages.split(',');
|
||||
for (const wantedLang of passedLangs) {
|
||||
for (const langId in languages) {
|
||||
const lang = languages[langId];
|
||||
if (lang.id === wantedLang || lang.name === wantedLang || lang.alias === wantedLang) {
|
||||
filteredLangs[lang.id] = lang;
|
||||
const languages = (() => {
|
||||
if (defArgs.wantedLanguages) {
|
||||
const filteredLangs: Partial<Record<LanguageKey, Language>> = {};
|
||||
const passedLangs = defArgs.wantedLanguages.split(',');
|
||||
for (const wantedLang of passedLangs) {
|
||||
for (const lang of Object.values(allLanguages)) {
|
||||
if (lang.id === wantedLang || lang.name === wantedLang || lang.alias === wantedLang) {
|
||||
filteredLangs[lang.id] = lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always keep cmake for IDE mode, just in case
|
||||
filteredLangs[allLanguages.cmake.id] = allLanguages.cmake;
|
||||
return filteredLangs;
|
||||
} else {
|
||||
return allLanguages;
|
||||
}
|
||||
// Always keep cmake for IDE mode, just in case
|
||||
filteredLangs[languages.cmake.id] = languages.cmake;
|
||||
languages = filteredLangs;
|
||||
}
|
||||
})();
|
||||
|
||||
if (Object.keys(languages).length === 0) {
|
||||
logger.error('Trying to start Compiler Explorer without a language');
|
||||
@@ -253,15 +258,15 @@ function staticHeaders(res) {
|
||||
}
|
||||
}
|
||||
|
||||
function contentPolicyHeader(/*res*/) {
|
||||
function contentPolicyHeader(res: express.Response) {
|
||||
// TODO: re-enable CSP
|
||||
// if (csp) {
|
||||
// res.setHeader('Content-Security-Policy', csp);
|
||||
// }
|
||||
}
|
||||
|
||||
function measureEventLoopLag(delayMs) {
|
||||
return new Promise(resolve => {
|
||||
function measureEventLoopLag(delayMs: number) {
|
||||
return new Promise<number>(resolve => {
|
||||
const start = process.hrtime.bigint();
|
||||
setTimeout(() => {
|
||||
const elapsed = process.hrtime.bigint() - start;
|
||||
@@ -301,11 +306,11 @@ function setupEventLoopLagLogging() {
|
||||
}
|
||||
}
|
||||
|
||||
let pugRequireHandler = () => {
|
||||
let pugRequireHandler: (path: string) => any = () => {
|
||||
logger.error('pug require handler not configured');
|
||||
};
|
||||
|
||||
async function setupWebPackDevMiddleware(router) {
|
||||
async function setupWebPackDevMiddleware(router: express.Router) {
|
||||
logger.info(' using webpack dev middleware');
|
||||
|
||||
/* eslint-disable node/no-unpublished-import,import/extensions, */
|
||||
@@ -313,8 +318,9 @@ async function setupWebPackDevMiddleware(router) {
|
||||
const {default: webpackConfig} = await import('./webpack.config.esm.js');
|
||||
const {default: webpack} = await import('webpack');
|
||||
/* eslint-enable */
|
||||
type WebpackConfiguration = ElementType<Parameters<typeof webpack>[0]>;
|
||||
|
||||
const webpackCompiler = webpack(webpackConfig);
|
||||
const webpackCompiler = webpack([webpackConfig as WebpackConfiguration]);
|
||||
router.use(
|
||||
webpackDevMiddleware(webpackCompiler, {
|
||||
publicPath: '/static',
|
||||
@@ -328,7 +334,7 @@ async function setupWebPackDevMiddleware(router) {
|
||||
pugRequireHandler = path => urljoin(httpRoot, 'static', path);
|
||||
}
|
||||
|
||||
async function setupStaticMiddleware(router) {
|
||||
async function setupStaticMiddleware(router: express.Router) {
|
||||
const staticManifest = await fs.readJson(path.join(distPath, 'manifest.json'));
|
||||
|
||||
if (staticUrl) {
|
||||
@@ -353,7 +359,7 @@ async function setupStaticMiddleware(router) {
|
||||
};
|
||||
}
|
||||
|
||||
function shouldRedactRequestData(data) {
|
||||
function shouldRedactRequestData(data: string) {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
return !parsed['allowStoreCodeDebug'];
|
||||
@@ -364,14 +370,14 @@ function shouldRedactRequestData(data) {
|
||||
|
||||
const googleShortUrlResolver = new ShortLinkResolver();
|
||||
|
||||
function oldGoogleUrlHandler(req, res, next) {
|
||||
function oldGoogleUrlHandler(req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const id = req.params.id;
|
||||
const googleUrl = `https://goo.gl/${encodeURIComponent(id)}`;
|
||||
googleShortUrlResolver
|
||||
.resolve(googleUrl)
|
||||
.then(resultObj => {
|
||||
const parsed = new url.URL(resultObj.longUrl);
|
||||
const allowedRe = new RegExp(ceProps('allowedShortUrlHostRe'));
|
||||
const allowedRe = new RegExp(ceProps<string>('allowedShortUrlHostRe'));
|
||||
if (parsed.host.match(allowedRe) === null) {
|
||||
logger.warn(`Denied access to short URL ${id} - linked to ${resultObj.longUrl}`);
|
||||
return next({
|
||||
@@ -394,13 +400,13 @@ function oldGoogleUrlHandler(req, res, next) {
|
||||
});
|
||||
}
|
||||
|
||||
function startListening(server) {
|
||||
function startListening(server: express.Express) {
|
||||
const ss = systemdSocket();
|
||||
let _port;
|
||||
if (ss) {
|
||||
// ms (5 min default)
|
||||
const idleTimeout = process.env.IDLE_TIMEOUT;
|
||||
const timeout = (idleTimeout === undefined ? 300 : idleTimeout) * 1000;
|
||||
const timeout = (idleTimeout === undefined ? 300 : parseInt(idleTimeout)) * 1000;
|
||||
if (idleTimeout) {
|
||||
const exit = () => {
|
||||
logger.info('Inactivity timeout reached, exiting.');
|
||||
@@ -440,7 +446,7 @@ function startListening(server) {
|
||||
}
|
||||
}
|
||||
|
||||
function setupSentry(sentryDsn) {
|
||||
function setupSentry(sentryDsn: string) {
|
||||
if (!sentryDsn) {
|
||||
logger.info('Not configuring sentry');
|
||||
return;
|
||||
@@ -489,7 +495,7 @@ async function main() {
|
||||
let prevCompilers;
|
||||
|
||||
if (opts.prediscovered) {
|
||||
const prediscoveredCompilersJson = await fs.readFile(opts.prediscovered);
|
||||
const prediscoveredCompilersJson = await fs.readFile(opts.prediscovered, 'utf8');
|
||||
initialCompilers = JSON.parse(prediscoveredCompilersJson);
|
||||
await compilerFinder.loadPrediscovered(initialCompilers);
|
||||
} else {
|
||||
@@ -538,7 +544,7 @@ async function main() {
|
||||
};
|
||||
|
||||
const noscriptHandler = new NoScriptHandler(router, handlerConfig);
|
||||
const routeApi = new RouteAPI(router, handlerConfig, noscriptHandler.renderNoScriptLayout);
|
||||
const routeApi = new RouteAPI(router, handlerConfig);
|
||||
|
||||
async function onCompilerChange(compilers) {
|
||||
if (JSON.stringify(prevCompilers) === JSON.stringify(compilers)) {
|
||||
@@ -616,7 +622,7 @@ async function main() {
|
||||
|
||||
loadSiteTemplates(configDir);
|
||||
|
||||
function renderConfig(extra, urlOptions) {
|
||||
function renderConfig(extra, urlOptions?) {
|
||||
const urlOptionsAllowed = ['readOnly', 'hideEditorToolbars', 'language'];
|
||||
const filteredUrlOptions = _.mapObject(_.pick(urlOptions, urlOptionsAllowed), val => utils.toProperty(val));
|
||||
const allExtraOptions = _.extend({}, filteredUrlOptions, extra);
|
||||
@@ -667,7 +673,7 @@ async function main() {
|
||||
);
|
||||
}
|
||||
|
||||
const embeddedHandler = function (req, res) {
|
||||
const embeddedHandler = function (req: express.Request, res: express.Response) {
|
||||
staticHeaders(res);
|
||||
contentPolicyHeader(res);
|
||||
res.render(
|
||||
@@ -684,7 +690,7 @@ async function main() {
|
||||
|
||||
await (isDevMode() ? setupWebPackDevMiddleware(router) : setupStaticMiddleware(router));
|
||||
|
||||
morgan.token('gdpr_ip', req => (req.ip ? utils.anonymizeIp(req.ip) : ''));
|
||||
morgan.token('gdpr_ip', (req: any) => (req.ip ? utils.anonymizeIp(req.ip) : ''));
|
||||
|
||||
// Based on combined format, but: GDPR compliant IP, no timestamp & no unused fields for our usecase
|
||||
const morganFormat = isDevMode() ? 'dev' : ':gdpr_ip ":method :url" :status';
|
||||
@@ -837,14 +843,14 @@ process.on('SIGINT', signalHandler('SIGINT'));
|
||||
process.on('SIGTERM', signalHandler('SIGTERM'));
|
||||
process.on('SIGQUIT', signalHandler('SIGQUIT'));
|
||||
|
||||
function signalHandler(name) {
|
||||
function signalHandler(name: string) {
|
||||
return () => {
|
||||
logger.info(`stopping process: ${name}`);
|
||||
process.exit(0);
|
||||
};
|
||||
}
|
||||
|
||||
function uncaughtHandler(err, origin) {
|
||||
function uncaughtHandler(err: Error, origin: NodeJS.UncaughtExceptionOrigin) {
|
||||
logger.info(`stopping process: Uncaught exception: ${err}\nException origin: ${origin}`);
|
||||
// The app will exit naturally from here, but if we call `process.exit()` we may lose log lines.
|
||||
// see https://github.com/winstonjs/winston/issues/1504#issuecomment-1033087411
|
||||
@@ -55,3 +55,6 @@ export function basic_comparator<T>(a: T, b: T) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/41253310/typescript-retrieve-element-type-information-from-array-type
|
||||
export type ElementType<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer T)[] ? T : never;
|
||||
|
||||
@@ -37,7 +37,7 @@ import {fileExists, resolvePathFromAppRoot} from './utils';
|
||||
|
||||
export class CompilerArguments implements ICompilerArguments {
|
||||
private readonly compilerId: string;
|
||||
private possibleArguments: PossibleArguments = {};
|
||||
public possibleArguments: PossibleArguments = {};
|
||||
private readonly maxPopularArguments = 5;
|
||||
private readonly storeSpecificArguments = false;
|
||||
private loadedFromFile = false;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class ApiHandler {
|
||||
private usedLangIds: LanguageKey[] = [];
|
||||
private options: ClientOptionsHandler | null = null;
|
||||
public readonly handle: express.Router;
|
||||
private readonly shortener: BaseShortener;
|
||||
public readonly shortener: BaseShortener;
|
||||
private release = {
|
||||
gitReleaseName: '',
|
||||
releaseBuildNumber: '',
|
||||
|
||||
@@ -51,7 +51,7 @@ export class NoScriptHandler {
|
||||
|
||||
formDataParser: ReturnType<typeof bodyParser.urlencoded> | undefined;
|
||||
|
||||
/* the type for config makes the most sense to define in app.js or api.js */
|
||||
/* the type for config makes the most sense to define in app.ts or api.ts */
|
||||
constructor(private readonly router: express.Router, config: any) {
|
||||
this.staticHeaders = config.staticHeaders;
|
||||
this.contentPolicyHeader = config.contentPolicyHeader;
|
||||
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@sentry/browser": "^7.28.1",
|
||||
"@sentry/node": "^7.28.1",
|
||||
"@types/morgan": "^1.9.4",
|
||||
"aws-sdk": "^2.1048.0",
|
||||
"big-integer": "^1.6.51",
|
||||
"body-parser": "^1.19.1",
|
||||
@@ -1916,6 +1917,14 @@
|
||||
"integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/morgan": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz",
|
||||
"integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
@@ -16971,6 +16980,14 @@
|
||||
"integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/morgan": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz",
|
||||
"integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
|
||||
15
package.json
15
package.json
@@ -14,7 +14,7 @@
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"main": "./app.js",
|
||||
"main": "./app.ts",
|
||||
"esm": {
|
||||
"mode": "all",
|
||||
"cache": false
|
||||
@@ -24,6 +24,7 @@
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@sentry/browser": "^7.28.1",
|
||||
"@sentry/node": "^7.28.1",
|
||||
"@types/morgan": "^1.9.4",
|
||||
"aws-sdk": "^2.1048.0",
|
||||
"big-integer": "^1.6.51",
|
||||
"body-parser": "^1.19.1",
|
||||
@@ -100,8 +101,8 @@
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/http-proxy": "^1.17.9",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"@types/jquery": "^3.5.10",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/node-targz": "^0.2.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
@@ -172,14 +173,14 @@
|
||||
"lint": "eslint --max-warnings=0 . --fix",
|
||||
"lint-check": "eslint --max-warnings=0 .",
|
||||
"lint-files": "eslint --max-warnings=0",
|
||||
"test": "mocha -b -r ts-node/register 'test/**/*.ts' 'test/**/*.js'",
|
||||
"test": "mocha -b -r ts-node/register 'test/**/*.ts' 'test/**/*.ts'",
|
||||
"test-min": "mocha -b --config .mocharc-min.yml",
|
||||
"fix": "npm run lint && npm run format && npm run ts-check",
|
||||
"check": "npm run ts-check && npm run lint-check && npm run test-min -- --reporter min",
|
||||
"dev": "cross-env NODE_ENV=DEV node -r esm -r ts-node/register app.js",
|
||||
"debugger": "cross-env NODE_ENV=DEV node --inspect -r esm -r ts-node/register app.js --debug",
|
||||
"debug": "cross-env NODE_ENV=DEV node -r esm -r ts-node/register app.js --debug",
|
||||
"start": "webpack && cross-env NODE_ENV=LOCAL node -r esm -r ts-node/register app.js",
|
||||
"dev": "cross-env NODE_ENV=DEV node -r esm -r ts-node/register app.ts",
|
||||
"debugger": "cross-env NODE_ENV=DEV node --inspect -r esm -r ts-node/register app.ts --debug",
|
||||
"debug": "cross-env NODE_ENV=DEV node -r esm -r ts-node/register app.ts --debug",
|
||||
"start": "webpack && cross-env NODE_ENV=LOCAL node -r esm -r ts-node/register app.ts",
|
||||
"codecov": "codecov --disable=gcov",
|
||||
"sentry": "npx -p @sentry/cli sentry-cli",
|
||||
"update-browserslist": "npx browserslist@latest -- --update-db",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"files": ["app.js", "compiler-args-app.ts"],
|
||||
"files": ["app.ts", "compiler-args-app.ts"],
|
||||
"include": ["**/*.js", "**/*.ts"],
|
||||
"exclude": ["out", "test", "etc", "examples", "static", "**/*.d.ts"],
|
||||
"compilerOptions": {
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"strictPropertyInitialization": false,
|
||||
"lib": ["dom", "es5", "dom.iterable"]
|
||||
},
|
||||
"exclude": ["out", "test", "etc", "examples", "app.js", "compiler-args-app.ts", "lib", "examples"]
|
||||
"exclude": ["out", "test", "etc", "examples", "app.ts", "compiler-args-app.ts", "lib", "examples"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user