Skip to content

Commit

Permalink
[INTERNAL] TypeScript: More Specification typings
Browse files Browse the repository at this point in the history
  • Loading branch information
RandomByte committed Aug 29, 2024
1 parent c720209 commit dc6c0e8
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 197 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"@types/graceful-fs": "^4.1.9",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.5.0",
"@types/semver": "^7.5.8",
"@types/sinon": "^17.0.3",
"@types/xml2js": "^0.4.14",
"ava": "^6.1.3",
Expand Down
15 changes: 15 additions & 0 deletions src/build/helpers/createBuildManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@ import {createRequire} from "node:module";
// Using CommonsJS require since JSON module imports are still experimental
const require = createRequire(import.meta.url);

export interface BuildManifest {
manifestVersion: string;
timestamp: string;
versions: {
builderVersion: string;
projectVersion: string;
fsVersion: string;
builderFsVersion?: string;
};
buildConfig: object;
version: string;
namespace: string;
tags: Record<string, string>;
}

/**
*
* @param pkg
Expand Down
15 changes: 11 additions & 4 deletions src/specifications/ComponentProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import * as resourceFactory from "@ui5/fs/resourceFactory";
import type ReaderCollection from "@ui5/fs/ReaderCollection";
import type AbstractReaderWriter from "@ui5/fs/AbstractReaderWriter";
import type AbstractReader from "@ui5/fs/AbstractReader";
// import type * as xml2jsModule from "xml2js";
// import {Parser} from "xml2js";

type MavenPropertyValue = string | Record<string, MavenProperty>;
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
Expand All @@ -18,6 +16,15 @@ interface Pom extends MavenProperty {
};
}

export interface Manifest {
"sap.app"?: {
id: string;
applicationVersion: {
version: string;
};
};
};

/**
* Subclass for projects potentially containing Components
*
Expand Down Expand Up @@ -166,9 +173,9 @@ abstract class ComponentProject extends Project {
/**
* Get a resource reader for the test resources of the project
*
* @returns Reader collection
* @returns Reader collection or null if no dedicated test path exists
*/
protected abstract _getTestReader(excludes: string[]): AbstractReader;
protected abstract _getTestReader(excludes: string[]): AbstractReader | null;

/**
* Get a resource reader/writer for accessing and modifying a project's resources
Expand Down
30 changes: 11 additions & 19 deletions src/specifications/Extension.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import Specification from "./Specification.js";
import Specification, {type SpecificationConfiguration, type SpecificationParameters} from "./Specification.js";

/**
* Extension
*
* @alias @ui5/project/specifications/Extension
* @hideconstructor
*/
class Extension extends Specification {
constructor(parameters) {
super(parameters);
if (new.target === Extension) {
throw new TypeError("Class 'Extension' is abstract. Please use one of the 'types' subclasses");
}
}
abstract class Extension<Configuration extends SpecificationConfiguration> extends Specification {
declare _config: Configuration;

/**
* @param parameters Specification parameters
Expand All @@ -21,26 +15,24 @@ class Extension extends Specification {
* @param parameters.modulePath File System path to access resources
* @param parameters.configuration Configuration object
*/
async init(parameters: {
id: string;
version: string;
modulePath: string;
configuration: object;
}) {
async init(parameters: SpecificationParameters) {
await super.init(parameters);

try {
await this._validateConfig();
} catch (err) {
throw new Error(
`Failed to validate configuration of ${this.getType()} extension ${this.getName()}: ` +
err.message);
if (err instanceof Error) {
throw new Error(
`Failed to validate configuration of ${this.getType()} extension ${this.getName()}: ` +
err.message);
}
throw err;
}

return this;
}

async _validateConfig() {}
protected abstract _validateConfig(): Promise<void>;
}

export default Extension;
19 changes: 15 additions & 4 deletions src/specifications/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import Specification, {type SpecificationConfiguration, type SpecificationParame
import type AbstractReader from "@ui5/fs/AbstractReader";
import type DuplexCollection from "@ui5/fs/DuplexCollection";
import ResourceTagCollection from "@ui5/fs/internal/ResourceTagCollection";
import {type BuildManifest} from "../build/helpers/createBuildManifest.js";

interface ProjectParameters extends SpecificationParameters {
buildManifest?: object;
buildManifest?: BuildManifest;
}

export interface ProjectConfiguration extends SpecificationConfiguration {
Expand All @@ -18,6 +19,7 @@ export interface ProjectConfiguration extends SpecificationConfiguration {
resources?: {
configuration?: {
propertiesFileSourceEncoding: string;
paths: Record<string, string>;
};
};
framework?: {
Expand All @@ -40,10 +42,19 @@ export interface ProjectConfiguration extends SpecificationConfiguration {
namespaces?: string[];
excludes?: string[];
};
libraryPreload?: {
excludes?: string[];
};
minification?: {
excludes?: string[];
};
jsdoc?: {
excludes?: string[];
};
bundles?: object[];
cachebuster?: {
signatureType: "time" | "hash";
};
};
server?: {
customMiddleware?: string[];
Expand All @@ -65,7 +76,7 @@ export interface ProjectReaderOptions {
*/
abstract class Project extends Specification {
_resourceTagCollection: ResourceTagCollection | null;
_buildManifest: object | undefined;
_buildManifest?: BuildManifest;
declare _config: ProjectConfiguration;

constructor() {
Expand Down Expand Up @@ -309,9 +320,9 @@ abstract class Project extends Specification {
*/
abstract getWorkspace(): DuplexCollection;

protected abstract _configureAndValidatePaths(config: object): Promise<void>;
protected abstract _configureAndValidatePaths(config: ProjectConfiguration): Promise<void>;

protected abstract _parseConfiguration(config: object, _buildManifest: object | undefined): Promise<void>;
protected abstract _parseConfiguration(config: ProjectConfiguration, buildManifest?: BuildManifest): Promise<void>;
}

export default Project;
2 changes: 1 addition & 1 deletion src/specifications/Specification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ abstract class Specification {
});
}

private async _dirExists(dirPath: string) {
protected async _dirExists(dirPath: string) {
const resource = await this.getRootReader().byPath(dirPath, {nodir: false});
if (resource?.getStatInfo().isDirectory()) {
return true;
Expand Down
26 changes: 7 additions & 19 deletions src/specifications/SpecificationVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ const SUPPORTED_VERSIONS = [
"4.0",
];

type SemverComparator = (a: string, b: string) => boolean;

/**
* Helper class representing a Specification Version. Featuring helper functions for easy comparison
* of versions.
*
* @alias @ui5/project/specifications/utils/SpecificationVersion
*/
class SpecificationVersion {
#specVersion;
Expand Down Expand Up @@ -182,33 +183,19 @@ class SpecificationVersion {
}
}

/**
*
* @param specVersion
*/
function getUnsupportedSpecVersionMessage(specVersion) {
function getUnsupportedSpecVersionMessage(specVersion: string) {
return `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` +
`For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`;
}

/**
*
* @param specVersion
*/
function getSemverCompatibleVersion(specVersion) {
function getSemverCompatibleVersion(specVersion: string) {
if (SpecificationVersion.isSupportedSpecVersion(specVersion)) {
return specVersion + ".0";
}
throw new Error(getUnsupportedSpecVersionMessage(specVersion));
}

/**
*
* @param comparator
* @param baseVersion
* @param testVersion
*/
function handleSemverComparator(comparator, baseVersion, testVersion) {
function handleSemverComparator(comparator: SemverComparator, baseVersion: string, testVersion: string) {
if (SPEC_VERSION_PATTERN.test(testVersion)) {
const a = baseVersion;
const b = testVersion + ".0";
Expand All @@ -221,4 +208,5 @@ export default SpecificationVersion;

// Export local function for testing only
export const __localFunctions__ = (process.env.NODE_ENV === "test") ?
{getSemverCompatibleVersion, handleSemverComparator} : /* istanbul ignore next */ undefined;
{getSemverCompatibleVersion, handleSemverComparator} :
/* istanbul ignore next */ undefined;
26 changes: 16 additions & 10 deletions src/specifications/extensions/ProjectShim.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
import Extension from "../Extension.js";
import {type SpecificationConfiguration} from "../Specification.js";

interface ShimConfiguration extends SpecificationConfiguration {
shims: {
dependencies?: Record<string, string>;
configurations?: Record<string, string>;
collections?: Record<string, {
modules: Record<string, string>;
}>;
};
}

/**
* ProjectShim
*
* @alias @ui5/project/specifications/extensions/ProjectShim
* @hideconstructor
*/
class ProjectShim extends Extension {
constructor(parameters) {
super(parameters);
}

class ProjectShim extends Extension<ShimConfiguration> {
/* === Attributes === */
/**
*/
public getDependencyShims() {
return this._config.shims.dependencies || {};
return this._config.shims.dependencies ?? {};
}

/**
*/
public getConfigurationShims() {
return this._config.shims.configurations || {};
return this._config.shims.configurations ?? {};
}

/**
*/
public getCollectionShims() {
return this._config.shims.collections || {};
return this._config.shims.collections ?? {};
}

private async _validateConfig() {
protected async _validateConfig() {
if (this._config.shims.collections) {
const {
default: path,
Expand Down
36 changes: 28 additions & 8 deletions src/specifications/extensions/ServerMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
import path from "node:path";
import Extension from "../Extension.js";
import {pathToFileURL} from "node:url";
import {type SpecificationConfiguration} from "../Specification.js";
import type AbstractReaderWriter from "@ui5/fs/AbstractReaderWriter";
import type Logger from "@ui5/logger/Logger";

interface ServerMiddlewareConfiguration extends SpecificationConfiguration {
middleware: {
path: string;
};
}

interface CustomMiddlewareParams {
log: Logger;
middlewareUtil: any;
options: Record<string, unknown>;
resources: {
workspace: AbstractReaderWriter;
};
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ExpressMiddleware = (req: any, res: any, next: any) => void;

type CustomMiddleware =
({log, middlewareUtil, options, resources}: CustomMiddlewareParams) => Promise<ExpressMiddleware>;

/**
* ServerMiddleware
*
* @alias @ui5/project/specifications/extensions/ServerMiddleware
* @hideconstructor
*/
class ServerMiddleware extends Extension {
constructor(parameters) {
super(parameters);
}

class ServerMiddleware extends Extension<ServerMiddlewareConfiguration> {
public async getMiddleware() {
const middlewarePath = path.join(this.getRootPath(), this._config.middleware.path);
const {default: middleware} = await import(pathToFileURL(middlewarePath));
const {default: middleware} = await import(pathToFileURL(middlewarePath)) as {default: CustomMiddleware};
return middleware;
}

private async _validateConfig() {
// eslint-disable-next-line @typescript-eslint/require-await
protected async _validateConfig() {
// TODO: Move to validator
if (/--\d+$/.test(this.getName())) {
throw new Error(`Server middleware name must not end with '--<number>'`);
Expand Down
Loading

0 comments on commit dc6c0e8

Please sign in to comment.