Skip to content

Commit

Permalink
build: add typechecking to instruments and modules (#8238)
Browse files Browse the repository at this point in the history
* build: add typechecking to instruments/modules

* ci: enable typechecking in PR builds

* build: bump some vite dependencies

* build: fix typechecking errors not showing up in igniter output

* build: bump dependencies

* fix: type fixes, part 1

* fix: type fixes, part 2

* build: throw non-tsc related error

* build: bump eslint related packages and remove unused deps

* fix: import and whitespace errors

* fix: workaround borked function-paren-newline rule

* fix(atsu): default return

* fix: Use ApproachType declaration rather than WT-specific type

* build: add bundles/ to .eslintignore

---------

Co-authored-by: Michael Corcoran <[email protected]>
Co-authored-by: Saschl <[email protected]>
  • Loading branch information
3 people authored Oct 7, 2023
1 parent bc03273 commit 7fbfd11
Show file tree
Hide file tree
Showing 93 changed files with 9,915 additions and 30,165 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ fbw-a380x/out/*
# TODO: these need to be more granular
fbw-a32nx/src/base/*
fbw-a380x/src/base/*
fbw-a32nx/bundles/*
fbw-a380x/bundles/*
1 change: 1 addition & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ jobs:
- name: Create .env file
run: |
echo A32NX_PRODUCTION_BUILD=1 >> fbw-a32nx/.env
echo FBW_TYPECHECK=1 >> fbw-a32nx/.env
echo CLIENT_ID=\"${{ secrets.NAVIGRAPH_CLIENT_ID }}\" >> fbw-a32nx/.env
echo CLIENT_SECRET=\"${{ secrets.NAVIGRAPH_CLIENT_SECRET }}\" >> fbw-a32nx/.env
echo CHARTFOX_SECRET=\"${{ secrets.CHARTFOX_SECRET }}\" >> fbw-a32nx/.env
Expand Down
88 changes: 85 additions & 3 deletions build-utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const dotenv = require('dotenv');
const dotenv = require("dotenv");
const path = require("path");
const childProcess = require("child_process");
const fs = require("fs");

function defineEnvVars() {
dotenv.config({ path: '.env.local' });
Expand Down Expand Up @@ -32,8 +34,11 @@ module.exports.defineEnvVars = defineEnvVars;
* @param globalName {string|undefined} the name of the global to define in the output IIFE
* @param entryPoint {string} the entrypoint path, as an absolute path
* @param outFile {string} the output file path, as a path relative to the root of the repository
* @param tsConfigDir {string?} the directory containing the tsconfig.json file to use, optionally
*
* @returns {import('esbuild').BuildOptions}
*/
function esbuildModuleBuild(projectRoot, globalName, entryPoint, outFile) {
function esbuildModuleBuild(projectRoot, globalName, entryPoint, outFile, tsConfigDir) {
const isProductionBuild = process.env.A32NX_PRODUCTION_BUILD === '1';

process.chdir(projectRoot);
Expand All @@ -43,6 +48,12 @@ function esbuildModuleBuild(projectRoot, globalName, entryPoint, outFile) {

define: { DEBUG: 'false', ...defineEnvVars() },

plugins: [
typecheckingPlugin(),
],

tsconfig: tsConfigDir !== undefined ? path.join(tsConfigDir, 'tsconfig.json') : undefined,

entryPoints: [entryPoint],
bundle: true,
treeShaking: false,
Expand All @@ -60,4 +71,75 @@ function esbuildModuleBuild(projectRoot, globalName, entryPoint, outFile) {
};
}

module.exports.esbuildModuleBuild = esbuildModuleBuild;
module.exports.createModuleBuild = esbuildModuleBuild;

/**
* Returns an esbuild plugin which runs `tsc` typechecking
*
* @returns {import('esbuild').Plugin}
*/
function typecheckingPlugin() {
return {
name: 'typecheck',
/**
* @param build {import('esbuild').PluginBuild}
*/
setup(build) {
build.onStart(() => {
if (!(process.env.FBW_TYPECHECK === '1' || process.env.FBW_TYPECHECK?.toLowerCase() === 'true')) {
return;
}

const { entryPoints } = build.initialOptions;
const entryPointDir = path.dirname(entryPoints[0]);

const tsConfigInEntryPointDir = fs.existsSync(path.join(entryPointDir, 'tsconfig.json'));

let tsConfigDir;
if (tsConfigInEntryPointDir) {
tsConfigDir = entryPointDir;
} else if (build.initialOptions.tsconfig !== undefined) {
tsConfigDir = path.dirname(build.initialOptions.tsconfig);
}

if (tsConfigDir === undefined) {
throw new Error(`Cannot run typechecking: no tsconfig.json file found in '${entryPointDir}' and tsconfig path not specified`);
}

/**
* @type {import('esbuild').PartialMessage[]}
*/
const errors = []

try {
childProcess.execSync('npx tsc --noEmit -p .', { cwd: tsConfigDir });
} catch (e) {
if (!('stdout' in e) || !e.stdout) {
throw e;
}

const tscErrors = e.stdout.toString().split('\n').filter((err) => err.trim() !== '');

errors.push(...tscErrors.map((err) => {
const match = /(.+)\((\d+),(\d+)\):\s+(.+)/.exec(err.trim());

if (match) {
const [, file, line, column, text] = match;

const filePath = path.resolve(tsConfigDir, file);
const lineText = fs.readFileSync(filePath).toString().split('\n')[line - 1];

return { text, location: { file, line: parseInt(line), column: parseInt(column) - 1, lineText } }
} else {
return { text: err };
}
}));
}

return { errors };
})
}
}
}

module.exports.typecheckingPlugin = typecheckingPlugin;
4 changes: 4 additions & 0 deletions fbw-a32nx/mach.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const postCssPlugin = require('esbuild-style-plugin');
const tailwind = require('tailwindcss');
const postCssColorFunctionalNotation = require('postcss-color-functional-notation');
const postCssInset = require('postcss-inset');
const { typecheckingPlugin } = require("#build-utils");

// process.env.FBW_TYPECHECK = "1";

/** @type { import('@synaptic-simulations/mach').MachConfig } */
module.exports = {
Expand All @@ -24,6 +27,7 @@ module.exports = {
],
}
}),
typecheckingPlugin(),
],
instruments: [
msfsAvionicsInstrument('PFD'),
Expand Down
8 changes: 5 additions & 3 deletions fbw-a32nx/src/systems/atsu/common/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

const esbuild = require('esbuild');
const path = require('path');
const { esbuildModuleBuild } = require('#build-utils');
const { createModuleBuild } = require('#build-utils');

const rootDir = path.join(__dirname, '..', '..', '..', '..');
const rootDir = path.join(__dirname, '..', '..', '..', '..', '..');
const outFile = 'fbw-a32nx/out/flybywire-aircraft-a320-neo/html_ui/JS/fbw-a32nx/atsu/common.js';

esbuild.build(esbuildModuleBuild('fbw-a32nx', 'AtsuCommon', path.join(rootDir, '../fbw-common/src/systems/datalink/common/src/index.ts'), outFile));
const srcDir = 'fbw-common/src/systems/datalink/common';

esbuild.build(createModuleBuild('fbw-a32nx', 'AtsuCommon', path.join(rootDir, srcDir, '/src/index.ts'), outFile, path.join(rootDir, srcDir)));
4 changes: 2 additions & 2 deletions fbw-a32nx/src/systems/atsu/fmsclient/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

const esbuild = require('esbuild');
const path = require('path');
const { esbuildModuleBuild } = require('#build-utils');
const { createModuleBuild } = require('#build-utils');

const outFile = 'fbw-a32nx/out/flybywire-aircraft-a320-neo/html_ui/JS/fbw-a32nx/atsu/fmsclient.js';

esbuild.build(esbuildModuleBuild('fbw-a32nx', 'AtsuFmsClient', path.join(__dirname, 'src/index.ts'), outFile));
esbuild.build(createModuleBuild('fbw-a32nx', 'AtsuFmsClient', path.join(__dirname, 'src/index.ts'), outFile, __dirname));
8 changes: 4 additions & 4 deletions fbw-a32nx/src/systems/atsu/fmsclient/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ export class FmsClient {
});
}

public getDatalinkStatus(value: string): DatalinkStatusCode {
public getDatalinkStatus(value: 'vhf' | 'satcom' | 'hf'): DatalinkStatusCode {
switch (value) {
case 'vhf':
return this.datalinkStatus.vhf;
Expand All @@ -491,11 +491,11 @@ export class FmsClient {
case 'hf':
return this.datalinkStatus.hf;
default:
return 99;
return DatalinkStatusCode.NotInstalled;
}
}

public getDatalinkMode(value: string): DatalinkModeCode {
public getDatalinkMode(value: 'vhf' | 'satcom' | 'hf'): DatalinkModeCode {
switch (value) {
case 'vhf':
return this.datalinkMode.vhf;
Expand All @@ -504,7 +504,7 @@ export class FmsClient {
case 'hf':
return this.datalinkMode.hf;
default:
return 99;
return DatalinkModeCode.None;
}
}
}
4 changes: 2 additions & 2 deletions fbw-a32nx/src/systems/extras-host/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

const esbuild = require('esbuild');
const path = require('path');
const { esbuildModuleBuild } = require('#build-utils');
const { createModuleBuild } = require('#build-utils');

const outFile = 'fbw-a32nx/out/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/A32NX/ExtrasHost/index.js';

esbuild.build(esbuildModuleBuild('fbw-a32nx', undefined, path.join(__dirname, './index.ts'), outFile));
esbuild.build(createModuleBuild('fbw-a32nx', undefined, path.join(__dirname, './index.ts'), outFile, __dirname));
28 changes: 16 additions & 12 deletions fbw-a32nx/src/systems/extras-host/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
{
"extends": "../tsconfig.json",

"compilerOptions": {
"incremental": false /* Enables incremental builds */,
"target": "es2017" /* Specifies the ES2017 target, compatible with Coherent GT */,
"module": "es2015" /* Ensures that modules are at least es2015 */,
"strict": false /* Enables strict type checking, highly recommended but optional */,
"esModuleInterop": true /* Emits additional JS to work with CommonJS modules */,
"skipLibCheck": true /* Skip type checking on library .d.ts files */,
"forceConsistentCasingInFileNames": true /* Ensures correct import casing */,
"moduleResolution": "node" /* Enables compatibility with MSFS SDK bare global imports */,
"jsxFactory": "FSComponent.buildComponent" /* Required for FSComponent framework JSX */,
"jsxFragmentFactory": "FSComponent.Fragment" /* Required for FSComponent framework JSX */,
"jsx": "react" /* Required for FSComponent framework JSX */
"paths": {
"@datalink/aoc": ["../../../fbw-common/src/systems/datalink/aoc/src/index.ts"],
"@datalink/atc": ["../../../fbw-common/src/systems/datalink/atc/src/index.ts"],
"@datalink/common": ["../../../fbw-common/src/systems/datalink/common/src/index.ts"],
"@datalink/router": ["../../../fbw-common/src/systems/datalink/router/src/index.ts"],
"@failures": ["./failures/src/index.ts"],
"@fmgc/*": ["./fmgc/src/*"],
"@instruments/common/*": ["./instruments/src/Common/*"],
"@localization/*": ["../localization/*"],
"@sentry/*": ["./sentry-client/src/*"],
"@simbridge/*": ["./simbridge-client/src/*"],
"@shared/*": ["./shared/src/*"],
"@tcas/*": ["./tcas/src/*"],
"@typings/*": ["../../../fbw-common/src/typings/*"],
"@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index-no-react.ts"]
}
}
}
4 changes: 2 additions & 2 deletions fbw-a32nx/src/systems/fmgc/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

const esbuild = require('esbuild');
const path = require('path');
const { esbuildModuleBuild } = require('#build-utils');
const { createModuleBuild } = require('#build-utils');

const outFile = 'fbw-a32nx/out/flybywire-aircraft-a320-neo/html_ui/JS/fbw-a32nx/fmgc/fmgc.js';

esbuild.build(esbuildModuleBuild('fbw-a32nx', 'Fmgc', path.join(__dirname, 'src/index.ts'), outFile));
esbuild.build(createModuleBuild('fbw-a32nx', 'Fmgc', path.join(__dirname, 'src/index.ts'), outFile, __dirname));
5 changes: 4 additions & 1 deletion fbw-a32nx/src/systems/fmgc/src/components/EfisLabels.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { FlightLevel } from '@fmgc/guidance/vnav/verticalFlightPlan/VerticalFlightPlan';
// Copyright (c) 2021-2023 FlyByWire Simulations
//
// SPDX-License-Identifier: GPL-3.0

import { FlightPlanManager } from '@fmgc/wtsdk';
import { FmgcComponent } from './FmgcComponent';

Expand Down
4 changes: 2 additions & 2 deletions fbw-a32nx/src/systems/fmgc/src/efis/EfisSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
//
// SPDX-License-Identifier: GPL-3.0

import { GenericDataListenerSync, LegType, RunwaySurface, TurnDirection, VorType } from '@flybywiresim/fbw-sdk';

import { FlightPlanManager, WaypointConstraintType } from '@fmgc/flightplanning/FlightPlanManager';
import { EfisOption, EfisNdMode, NdSymbol, NdSymbolTypeFlags, EfisNdRangeValue, rangeSettings } from '@shared/NavigationDisplay';
import { GuidanceManager } from '@fmgc/guidance/GuidanceManager';
import { Coordinates } from '@fmgc/flightplanning/data/geo';
import { GuidanceController } from '@fmgc/guidance/GuidanceController';
import { SegmentType } from '@fmgc/wtsdk';
import { GenericDataListenerSync } from '@flybywiresim/fbw-sdk';
import { LnavConfig } from '@fmgc/guidance/LnavConfig';
import { NearbyFacilities } from '@fmgc/navigation/NearbyFacilities';
import { NavaidTuner } from '@fmgc/navigation/NavaidTuner';
import { getFlightPhaseManager } from '@fmgc/flightphase';
import { FmgcFlightPhase } from '@shared/flightphase';
import { LegType, RunwaySurface, TurnDirection, VorType } from '../types/fstypes/FSEnums';

export class EfisSymbols {
/** these types of legs are current not integrated into the normal symbol drawing routines */
Expand Down
42 changes: 2 additions & 40 deletions fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/

import { NXDataStore } from '@flybywiresim/fbw-sdk';
import { LegType } from '@fmgc/types/fstypes/FSEnums';
import { LegType } from '@flybywiresim/fbw-sdk';
import { LnavConfig } from '@fmgc/guidance/LnavConfig';
import { ApproachStats, HoldData } from '@fmgc/flightplanning/data/flightplan';
import { SegmentType } from '@fmgc/wtsdk';
Expand Down Expand Up @@ -197,8 +197,6 @@ export class FlightPlanManager {
* Loads the flight plans from data storage.
*/
public _loadFlightPlans(): void {
this._getFlightPlan();

if (this._flightPlans.length === 0) {
const newFpln = new ManagedFlightPlan();
newFpln.setParentInstrument(this._parentInstrument);
Expand Down Expand Up @@ -593,7 +591,7 @@ export class FlightPlanManager {
/**
* Gets the currently selected departure information for the current flight plan.
*/
public getDeparture(flightPlanIndex = NaN): WayPoint | undefined {
public getDeparture(flightPlanIndex = NaN): RawDeparture | undefined {
const origin = this.getOrigin();
if (Number.isNaN(flightPlanIndex)) {
flightPlanIndex = this._currentFlightPlanIndex;
Expand Down Expand Up @@ -1771,32 +1769,6 @@ export class FlightPlanManager {
return null;
}

/**
* Gets the current stored flight plan
*/
public _getFlightPlan(): void {
if (!LnavConfig.DEBUG_SAVE_FPLN_LOCAL_STORAGE) {
return;
}
const fpln = window.localStorage.getItem(FlightPlanManager.FlightPlanKey);
if (fpln === null || fpln === '') {
this._flightPlans = [];
const initFpln = new ManagedFlightPlan();
initFpln.setParentInstrument(this._parentInstrument);
this._flightPlans.push(initFpln);
} else if (window.localStorage.getItem(FlightPlanManager.FlightPlanCompressedKey) === '1') {
this._flightPlans = JSON.parse(LZUTF8.decompress(fpln, { inputEncoding: 'StorageBinaryString' }));
} else {
try {
this._flightPlans = JSON.parse(fpln);
} catch (e) {
// Assume we failed because compression status did not match up. Try to decompress anyway.

this._flightPlans = JSON.parse(LZUTF8.decompress(fpln, { inputEncoding: 'StorageBinaryString' }));
}
}
}

public getCurrentFlightPlan(): ManagedFlightPlan {
return this._flightPlans[this._currentFlightPlanIndex];
}
Expand All @@ -1813,16 +1785,6 @@ export class FlightPlanManager {
return;
}

if (LnavConfig.DEBUG_SAVE_FPLN_LOCAL_STORAGE) {
let fpJson = JSON.stringify(this._flightPlans.map((fp) => fp.serialize()));
if (fpJson.length > 2500000) {
fpJson = LZUTF8.compress(fpJson, { outputEncoding: 'StorageBinaryString' });
window.localStorage.setItem(FlightPlanManager.FlightPlanCompressedKey, '1');
} else {
window.localStorage.setItem(FlightPlanManager.FlightPlanCompressedKey, '0');
}
window.localStorage.setItem(FlightPlanManager.FlightPlanKey, fpJson);
}
SimVar.SetSimVarValue(FlightPlanManager.FlightPlanVersionKey, 'number', ++this._currentFlightPlanVersion);
if (NXDataStore.get('FP_SYNC', 'LOAD') === 'SAVE') {
FlightPlanAsoboSync.SaveToGame(this).catch(console.error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@ export enum SegmentType {
Missed,

/** The destination airfield segment. */
Destination
Destination,

/** An empty segment */
Empty = -1,
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

import { HoldData, HoldType } from '@fmgc/flightplanning/data/flightplan';
import { firstSmallCircleIntersection } from 'msfs-geo';
import { AltitudeDescriptor, FixTypeFlags, LegType } from '../types/fstypes/FSEnums';
import { AltitudeDescriptor, FixTypeFlags, LegType } from '@flybywiresim/fbw-sdk';
import { FixNamingScheme } from './FixNamingScheme';
import { GeoMath } from './GeoMath';
import { RawDataMapper } from './RawDataMapper';
Expand Down
Loading

0 comments on commit 7fbfd11

Please sign in to comment.