mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 01:41:05 -05:00
Added type descriptors for Action parameters
This commit is contained in:
parent
e55d6ffbda
commit
4dd97a775d
1 changed files with 157 additions and 0 deletions
157
src/utils/actions/action-parameter-type-descriptor.ts
Normal file
157
src/utils/actions/action-parameter-type-descriptor.ts
Normal file
|
@ -0,0 +1,157 @@
|
|||
import { QueryString } from "@/utils/net";
|
||||
import { ImportDirective, parseImportDirective } from "@/utils/reflection";
|
||||
|
||||
/**
|
||||
* Represents a type of a parameter of a GitHub Action.
|
||||
*/
|
||||
export interface ActionParameterTypeDescriptor extends ImportDirective {
|
||||
/**
|
||||
* Indicates whether the type should be treated as an array.
|
||||
*/
|
||||
isArray: boolean;
|
||||
|
||||
/**
|
||||
* A factory function able to create a new instance of the this type.
|
||||
*/
|
||||
factory?: ImportDirective;
|
||||
|
||||
/**
|
||||
* Options for the factory function.
|
||||
*
|
||||
* Known values include:
|
||||
* - `split` (boolean) - Determines if the input string should be split into an array of strings.
|
||||
* Defaults to `true` if this type represents an array, and `false` otherwise.
|
||||
*
|
||||
* - `separator` (RegExp) - If `split` is `true`, this is used to divide the input string into an array of strings.
|
||||
* Otherwise, it's unused. Default value is `/\r?\n/g`.
|
||||
*
|
||||
* - `processSeparately` (boolean) - If `split` is set to `true`, this indicates whether the factory/converter function
|
||||
* should accept the input array as a whole or process its values individually and then concatenate them into a new array.
|
||||
* Default value is the same as `split`.
|
||||
*
|
||||
* - `trimEntries` (boolean) - If `true`, trims whitespace from the beginning and end of each entry in the array.
|
||||
* Default value is the same as `split`.
|
||||
*
|
||||
* - `removeEmptyEntries` (boolean) - If `true`, removes empty entries from the array after processing.
|
||||
* Default value is the same as `split`.
|
||||
*
|
||||
* - `flatDepth` (number) - The depth level specifying how deep a nested array structure should be flattened.
|
||||
* Passes the value to Array.prototype.flat() method. Default value is `1`.
|
||||
*/
|
||||
options?: QueryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string into a type descriptor of a parameter of a GitHub Action.
|
||||
*
|
||||
* @param descriptor - The type descriptor to parse.
|
||||
*
|
||||
* @returns An {@link ActionParameterTypeDescriptor} object, or `undefined` if the input was invalid.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* parseActionParameterTypeDescriptor("foo/bar-bar/baz-baz->{BazBaz}:{parseBazBaz}?key=value");
|
||||
* ```
|
||||
*/
|
||||
export function parseActionParameterTypeDescriptor(descriptor: string): ActionParameterTypeDescriptor | undefined {
|
||||
if (!descriptor) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Remove all whitespace characters and generics from the descriptor.
|
||||
descriptor = descriptor.replaceAll(/\s/g, "").replaceAll(/<.*>/g, "");
|
||||
|
||||
const isArray = descriptor.includes("[]");
|
||||
descriptor = descriptor.replaceAll("[]", "");
|
||||
|
||||
const optionsIndex = descriptor.indexOf("?");
|
||||
const options = optionsIndex >= 0 ? QueryString.parse(descriptor.substring(optionsIndex)) : undefined;
|
||||
descriptor = optionsIndex >= 0 ? descriptor.substring(0, optionsIndex) : descriptor;
|
||||
|
||||
const normalizedDescriptor = normalizePattern(descriptor);
|
||||
const [type, factory] = normalizedDescriptor.split(";").map(parseImportDirective);
|
||||
|
||||
return { ...type, factory, options, isArray };
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a type descriptor pattern for use in an action signature.
|
||||
*
|
||||
* @param descriptor - The type descriptor pattern to normalize.
|
||||
*
|
||||
* @returns The normalized type descriptor pattern.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* "foo/bar-bar/baz-baz->{BazBaz:parseBazBaz}" => "foo/bar-bar/baz-baz->{BazBaz};parseBazBaz"
|
||||
* "foo/bar-bar/baz-baz->{BazBaz}:{parseBazBaz}" => "foo/bar-bar/baz-baz->{BazBaz};foo/bar-bar/baz-baz->{parseBazBaz}"
|
||||
* "foo/bar-bar/baz-baz->{BazBaz}:parseBazBaz" => "foo/bar-bar/baz-baz->{BazBaz};foo/bar-bar/baz-baz->parseBazBaz"
|
||||
* "foo/bar-bar/baz-baz->BazBaz:{parseBazBaz}" => "foo/bar-bar/baz-baz->BazBaz;foo/bar-bar/baz-baz->{parseBazBaz}"
|
||||
* "foo/bar-bar/baz-baz->BazBaz:parseBazBaz" => "foo/bar-bar/baz-baz->BazBaz;parseBazBaz"
|
||||
* ```
|
||||
*/
|
||||
function normalizePattern(descriptor: string): string {
|
||||
if (!descriptor.includes("/") && descriptor.includes(".")) {
|
||||
return normalizePatternInDotNotation(descriptor);
|
||||
}
|
||||
|
||||
if (!descriptor.includes(":")) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
const descriptors = descriptor.split(";");
|
||||
const mainDescriptor = descriptors[0];
|
||||
if (!mainDescriptor.includes(":")) {
|
||||
return mainDescriptor;
|
||||
}
|
||||
|
||||
const typeDescriptor = mainDescriptor.replaceAll(/:(?:\w+|{\w+})/g, "");
|
||||
const typeModule = mainDescriptor.match(/^.*->/)?.[0];
|
||||
const match = mainDescriptor.match(/\{?(?<type>\w+)(?<isTypeImported>\}?):(?<isFactoryImported>\{?)(?<factory>\w+)\}?/)?.groups;
|
||||
const factoryModule = !match.isTypeImported && !match.isFactoryImported ? "" : typeModule;
|
||||
const factoryDescriptor = factoryModule + (match.isFactoryImported ? `{${match.factory}}` : match.factory);
|
||||
return `${typeDescriptor};${factoryDescriptor}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a type descriptor in dot notation to slash notation.
|
||||
*
|
||||
* @param descriptor - The type descriptor in dot notation.
|
||||
*
|
||||
* @returns The type descriptor in slash notation.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* "foo.barBar.BazBaz" => "foo/bar-bar/baz-baz->{BazBaz}"
|
||||
* "foo.barBar.BazBaz:parseBazBaz" => "foo/bar-bar/baz-baz->{BazBaz};parseBazBaz"
|
||||
* "foo.barBar.BazBaz:{parseBazBaz}" => "foo/bar-bar/baz-baz->{BazBaz};foo/bar-bar/baz-baz->{parseBazBaz}""
|
||||
* ```
|
||||
*/
|
||||
function normalizePatternInDotNotation(descriptor: string): string {
|
||||
const descriptors = descriptor.split(";");
|
||||
const dotDescriptor = descriptors[0];
|
||||
|
||||
// Use a regular expression to match the path, name, and factory components of the descriptor.
|
||||
const match = dotDescriptor.match(/^(?<path>.*?\.)?(?<name>[\w]+?)(?::(?<factory>{?\w+}?))?$/);
|
||||
if (!match) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
const path = match.groups.path;
|
||||
const typeName = match.groups.name;
|
||||
|
||||
// Create the full path by replacing dots with slashes and converting camelCase to kebab-case.
|
||||
const fullPath = path && `${path}${typeName}`.replaceAll(".", "/").replaceAll(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
|
||||
const factoryName = match.groups.factory;
|
||||
const isFactoryImported = factoryName?.charAt(0) === "{";
|
||||
const typeDescriptor = fullPath ? `${fullPath}->{${typeName}}` : typeName;
|
||||
const factoryDescriptor = factoryName ? isFactoryImported && fullPath ? `${fullPath}->${factoryName}` : factoryName : descriptors[1];
|
||||
|
||||
// Join the type and factory descriptors (if any) with semicolons and return as the result.
|
||||
const slashDescriptor = [typeDescriptor, factoryDescriptor].filter(x => x).join(";");
|
||||
return slashDescriptor;
|
||||
}
|
Loading…
Reference in a new issue