Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refinements to entity references #7

Merged
merged 1 commit into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"singleQuote": false,
"trailingComma": "all",
"jsxSingleQuote": false,
"printWidth": 100,
"arrowParens": "avoid"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@digital-alchemy/type-writer",
"repository": "https://github.com/Digital-Alchemy-TS/type-writer",
"homepage": "https://docs.digital-alchemy.app/Type-Writer",
"version": "0.3.8",
"version": "0.3.9",
"scripts": {
"build": "rm -rf dist/; tsc",
"lint": "eslint src",
Expand Down
5 changes: 2 additions & 3 deletions src/build.extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { is, TServiceParams } from "@digital-alchemy/core";
import { PICK_ENTITY } from "@digital-alchemy/hass";

Check failure on line 2 in src/build.extension.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

'PICK_ENTITY' is declared but its value is never read.
import { existsSync, writeFileSync } from "fs";
import { join } from "path";
import { exit } from "process";
Expand Down Expand Up @@ -47,9 +48,7 @@
const typeInterface = await type_writer.call_service();
const entities = await hass.fetch.getAllEntities();
const entitySetup = {};
entities.forEach(i =>
internal.utils.object.set(entitySetup, i.entity_id, i),
);
entities.forEach(i => internal.utils.object.set(entitySetup, i.entity_id, i));
return [
`// This file is generated, and is automatically updated as a npm post install step`,
"// Do not edit this file, it will only affect type definitions, not functional code",
Expand Down
192 changes: 58 additions & 134 deletions src/i-call-service.extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,17 @@
HassServiceDTO,
ServiceListField,
ServiceListFieldDescription,
ServiceListSelector,
ServiceListServiceTarget,
} from "@digital-alchemy/hass";
import { dump } from "js-yaml";
import {
addSyntheticLeadingComment,
factory,
SyntaxKind,
TypeElement,
TypeNode,
} from "typescript";
export async function ICallServiceExtension({
hass,
logger,
type_writer,
}: TServiceParams) {
import { addSyntheticLeadingComment, factory, SyntaxKind, TypeElement, TypeNode } from "typescript";
export async function ICallServiceExtension({ hass, logger, type_writer }: TServiceParams) {
return async function () {
const domains = await hass.fetch.listServices();

// #MARK: createTarget
function createTarget(
target: ServiceListServiceTarget,
fallbackDomain: string,
) {
function createTarget(target: ServiceListServiceTarget, fallbackDomain: string) {
if (is.empty(target)) {
return undefined;
}
Expand All @@ -40,9 +28,7 @@
return addSyntheticLeadingComment(
property,
SyntaxKind.MultiLineCommentTrivia,
"*\n" +
["Assisted definition"].map(i => ` * ${i}`).join(`\n`) +
"\n ",
"*\n" + ["Assisted definition"].map(i => ` * ${i}`).join(`\n`) + "\n ",
true,
);
}
Expand All @@ -65,24 +51,14 @@
*
* This block is specifically for refining the `entity_id` type definitions
*/
function generateEntityList(
target: ServiceListServiceTarget,
fallbackDomain: string,
) {
const isEmpty =
is.empty(target.entity) || target.entity.every(i => is.empty(i));
function generateEntityList(target: ServiceListServiceTarget, fallbackDomain: string) {
const isEmpty = is.empty(target.entity) || target.entity.every(i => is.empty(i));
if (isEmpty) {
return factory.createParenthesizedType(
factory.createUnionTypeNode([
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
undefined,
),
factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), undefined),
factory.createArrayTypeNode(
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
undefined,
),
factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), undefined),
),
]),
);
Expand All @@ -91,28 +67,39 @@
const domainReference = domain?.shift() ?? fallbackDomain;
return factory.createParenthesizedType(
factory.createUnionTypeNode([
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
[
factory.createLiteralTypeNode(
factory.createStringLiteral(domainReference),
),
],
),
factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), [
factory.createLiteralTypeNode(factory.createStringLiteral(domainReference)),
]),
factory.createArrayTypeNode(
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
[
factory.createLiteralTypeNode(
factory.createStringLiteral(domainReference),
),
],
),
factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), [
factory.createLiteralTypeNode(factory.createStringLiteral(domainReference)),
]),
),
]),
);
}

// #MARK: buildEntityReference
function buildEntityReference(domain: string, selector: ServiceListSelector) {
let node: TypeNode;
const type = is.empty(domain)
? factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"))
: factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), [
factory.createLiteralTypeNode(factory.createStringLiteral(domain)),
]);

if (selector?.entity?.multiple) {

Check failure on line 91 in src/i-call-service.extension.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Property 'multiple' does not exist on type 'ServiceListSelectorTarget'.
node = factory.createArrayTypeNode(type);
} else {
node = is.empty(domain)
? type
: factory.createParenthesizedType(
factory.createUnionTypeNode([type, factory.createArrayTypeNode(type)]),
);
}
return node;
}

// #MARK: fieldPropertySignature
function fieldPropertySignature(
parameterName: string,
Expand All @@ -133,38 +120,14 @@
node = factory.createKeywordTypeNode(SyntaxKind.StringKeyword);
// string | `domain.${keyof typeof ENTITY_SETUP.domain}`
else if (!is.undefined(selector?.entity))
node = is.empty(domain)
? factory.createKeywordTypeNode(SyntaxKind.StringKeyword)
: factory.createParenthesizedType(
factory.createUnionTypeNode([
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
[
factory.createLiteralTypeNode(
factory.createStringLiteral(domain),
),
],
),
factory.createArrayTypeNode(
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
[
factory.createLiteralTypeNode(
factory.createStringLiteral(domain),
),
],
),
),
]),
);
// some combination of:
// : PICK_ENTITY | PICK_ENTITY[] | PICK_ENTITY<"domain"> | PICK_ENTITY<"domain">[]
node = buildEntityReference(domain, selector);
// : "option" | "option" | "option" | "option"
else if (!is.undefined(selector?.select))
node = factory.createUnionTypeNode(
selector?.select.options.map(
(i: string | Record<"label" | "value", string>) =>
factory.createLiteralTypeNode(
factory.createStringLiteral(is.string(i) ? i : i.value),
),
selector?.select.options.map((i: string | Record<"label" | "value", string>) =>
factory.createLiteralTypeNode(factory.createStringLiteral(is.string(i) ? i : i.value)),
),
);
// : Record<string, unknown> | (unknown[]);
Expand All @@ -183,9 +146,7 @@
const property = factory.createPropertySignature(
undefined,
factory.createIdentifier(parameterName),
details.required
? undefined
: factory.createToken(SyntaxKind.QuestionToken),
details.required ? undefined : factory.createToken(SyntaxKind.QuestionToken),
node,
);
return addSyntheticLeadingComment(
Expand All @@ -211,32 +172,18 @@

return factory.createUnionTypeNode([
serviceDomain === "scene" && serviceName === "apply"
? factory.createTypeReferenceNode(
factory.createIdentifier("Partial"),
[
factory.createTypeReferenceNode(
factory.createIdentifier("Record"),
[
factory.createTypeReferenceNode(
factory.createIdentifier("PICK_ENTITY"),
undefined,
),
factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword),
],
),
],
)
: factory.createTypeReferenceNode(
factory.createIdentifier("Record"),
[
factory.createKeywordTypeNode(SyntaxKind.StringKeyword),
? factory.createTypeReferenceNode(factory.createIdentifier("Partial"), [
factory.createTypeReferenceNode(factory.createIdentifier("Record"), [
factory.createTypeReferenceNode(factory.createIdentifier("PICK_ENTITY"), undefined),
factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword),
],
),
]),
])
: factory.createTypeReferenceNode(factory.createIdentifier("Record"), [
factory.createKeywordTypeNode(SyntaxKind.StringKeyword),
factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword),
]),
factory.createParenthesizedType(
factory.createArrayTypeNode(
factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword),
),
factory.createArrayTypeNode(factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword)),
),
]);
}
Expand All @@ -254,14 +201,7 @@
...(is.empty(details.description) ? [] : ["", details.description]),
...(is.empty(example)
? []
: [
"",
`### Example`,
"",
"```json",
JSON.stringify(example, undefined, " "),
"```",
]),
: ["", `### Example`, "", "```json", JSON.stringify(example, undefined, " "), "```"]),
...(is.undefined(details.default)
? []
: [
Expand Down Expand Up @@ -290,23 +230,15 @@
function serviceComment(key: string, value: ServiceListField) {
return (
`*\n` +
[
`### ${value.name || key}`,
"",
...value.description.split("\n").map(i => `> ${i}`),
]
[`### ${value.name || key}`, "", ...value.description.split("\n").map(i => `> ${i}`)]
.map(i => ` * ${i}`)
.join(`\n`) +
"\n "
);
}

// #MARK: BuildServiceParameters
function serviceParameters(
domain: string,
key: string,
value: ServiceListField,
) {
function serviceParameters(domain: string, key: string, value: ServiceListField) {
return [
// f( service_data: { ...definition } )
// Provide this ^^^^^^
Expand All @@ -315,18 +247,14 @@
undefined,
factory.createIdentifier("service_data"),
// ? If all the parameters are optional, then don't require the data at all
Object.values(value.fields).some(i =>
is.boolean(i.required) ? !i.required : true,
)
Object.values(value.fields).some(i => (is.boolean(i.required) ? !i.required : true))
? factory.createToken(SyntaxKind.QuestionToken)
: undefined,
factory.createTypeLiteralNode(
[
...Object.entries(value.fields)
.sort(([a], [b]) => (a > b ? UP : DOWN))
.map(([service, details]) =>
fieldPropertySignature(service, details, domain, key),
),
.map(([service, details]) => fieldPropertySignature(service, details, domain, key)),
createTarget(value.target as ServiceListServiceTarget, domain),
].filter(i => !is.undefined(i)) as TypeElement[],
),
Expand All @@ -335,11 +263,7 @@
}

// #MARK: BuildService
function buildService(
domain: string,
key: string,
value: ServiceListField,
) {
function buildService(domain: string, key: string, value: ServiceListField) {
return addSyntheticLeadingComment(
factory.createMethodSignature(
undefined,
Expand Down
Loading
Loading