@typed-assistant/builder 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js ADDED
@@ -0,0 +1,6 @@
1
+ /** @type {import("eslint").Linter.Config} */
2
+ module.exports = {
3
+ root: true,
4
+ extends: ["@typed-assistant/eslint-config/react-internal.js"],
5
+ parserOptions: {},
6
+ }
@@ -0,0 +1 @@
1
+ $ eslint . --max-warnings 0
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @typed-assistant/builder
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - First version.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Home Assistant Configuration
2
+
3
+ This application requires two environment variables to be set: `HASS_SERVER` and `HASS_TOKEN`.
4
+
5
+ - `HASS_SERVER`: This should be the URL of your Home Assistant server. For example, `http://192.168.1.99:8123`.
6
+
7
+ - `HASS_TOKEN`: This is the API token generated from your Home Assistant instance. You can generate this token from your Home Assistant profile page. Please ensure to keep this token secure as it provides full access to your Home Assistant instance.
8
+
9
+ Please ensure these environment variables are set before running the application.
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@typed-assistant/builder",
3
+ "version": "0.0.1",
4
+ "exports": {
5
+ "./appProcess": "./src/appProcess.tsx",
6
+ "./bunInstall": "./src/bunInstall.tsx",
7
+ "./getSpawnText": "./src/getSpawnText.tsx",
8
+ "./pullChanges": "./src/pullChanges.tsx"
9
+ },
10
+ "scripts": {
11
+ "lint": "eslint . --max-warnings 0"
12
+ },
13
+ "dependencies": {
14
+ "@mdi/svg": "^7.3.67",
15
+ "ignore": "^5.3.0"
16
+ },
17
+ "devDependencies": {
18
+ "@typed-assistant/eslint-config": "workspace:*",
19
+ "@typed-assistant/logger": "workspace:*",
20
+ "@typed-assistant/typescript-config": "workspace:*",
21
+ "@typed-assistant/utils": "workspace:*",
22
+ "@types/node": "^20.10.6",
23
+ "@types/eslint": "^8.56.1",
24
+ "eslint": "^8.56.0",
25
+ "home-assistant-js-websocket": "^8.2.0",
26
+ "typescript": "^5.3.3"
27
+ },
28
+ "peerDependencies": {
29
+ "home-assistant-js-websocket": "^8.2.0"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public",
33
+ "registry": "https://registry.npmjs.org/"
34
+ }
35
+ }
@@ -0,0 +1,102 @@
1
+ import { generateTypes } from "@typed-assistant/types/generateTypes"
2
+ import { log } from "@typed-assistant/logger"
3
+ import type { Subprocess } from "bun"
4
+ import { readFileSync, watch } from "fs"
5
+ import { join, relative } from "path"
6
+ import ignore from "ignore"
7
+ import { ONE_SECOND } from "@typed-assistant/utils/durations"
8
+ import { getHassAPI } from "@typed-assistant/utils/getHassAPI"
9
+ import { pullChanges } from "./pullChanges"
10
+
11
+ type Processes = Awaited<ReturnType<typeof buildAndStartAppProcess>>
12
+
13
+ async function buildAndStartAppProcess(
14
+ appSourceFile?: string,
15
+ options?: Parameters<typeof generateTypes>[0],
16
+ ) {
17
+ await generateTypes({ mdiPaths: options?.mdiPaths })
18
+ return { app: await startApp(appSourceFile) }
19
+ }
20
+
21
+ async function startApp(appSourceFile: string = "src/entry.tsx") {
22
+ log("🚀 Starting app...")
23
+ const path = join(process.cwd(), appSourceFile)
24
+ return Bun.spawn(["bun", path], { stdout: "inherit" })
25
+ }
26
+
27
+ async function kill(process: Subprocess) {
28
+ log(`💀 Killing process: ${process.pid}`)
29
+ process.kill()
30
+ await process.exited
31
+ }
32
+
33
+ let settingUp = { current: false }
34
+ async function killAndRestartApp(subprocesses: Processes) {
35
+ if (settingUp.current) return subprocesses
36
+ log("♻️ Restarting app...")
37
+ settingUp.current = true
38
+ if (subprocesses.app) await kill(subprocesses.app)
39
+ const newSubprocesses = await buildAndStartAppProcess()
40
+ settingUp.current = false
41
+ return newSubprocesses
42
+ }
43
+
44
+ function setupWatcherInternal(...args: Parameters<typeof watch>) {
45
+ const [directory, callback] = args
46
+ if (typeof directory !== "string") throw new Error("Directory must be string")
47
+
48
+ log("👀 Watching directory:", directory)
49
+ const watcher = watch(directory, { recursive: true }, callback)
50
+
51
+ return watcher
52
+ }
53
+
54
+ export async function setupWatcher(
55
+ ...args: Parameters<typeof buildAndStartAppProcess>
56
+ ) {
57
+ await setupGitSync()
58
+
59
+ let subprocesses = await buildAndStartAppProcess(...args)
60
+
61
+ setupWatcherInternal(
62
+ join(process.cwd(), "src"),
63
+ async function onFileChange(event, filename) {
64
+ if (!filename) return
65
+ if (shouldIgnoreFileOrFolder(filename)) return
66
+ log(`⚠️ Change to ${filename} detected.`)
67
+ if (filename.endsWith("process.tsx")) {
68
+ await restartAddon()
69
+ } else {
70
+ subprocesses = await killAndRestartApp(subprocesses)
71
+ }
72
+ },
73
+ )
74
+
75
+ return subprocesses
76
+ }
77
+
78
+ const setupGitSync = async ({
79
+ gitPullPollDuration,
80
+ }: {
81
+ /** Duration in seconds */
82
+ gitPullPollDuration?: number
83
+ } = {}) => {
84
+ const duration = gitPullPollDuration ?? 5
85
+ await pullChanges()
86
+ log(` ⏳ Pulling changes again in ${duration} seconds...`)
87
+
88
+ setTimeout(() => {
89
+ setupGitSync({ gitPullPollDuration })
90
+ }, duration * ONE_SECOND)
91
+ }
92
+
93
+ const ig = ignore().add(
94
+ `${readFileSync(join(process.cwd(), ".gitignore")).toString()}\n.git`,
95
+ )
96
+ const shouldIgnoreFileOrFolder = (filename: string) =>
97
+ ig.ignores(relative(process.cwd(), filename))
98
+
99
+ const restartAddon = async () => {
100
+ log("♻️ Restarting addon...")
101
+ await getHassAPI(`http://supervisor/addons/self/restart`, { method: "POST" })
102
+ }
@@ -0,0 +1,21 @@
1
+ import { log } from "@typed-assistant/logger"
2
+
3
+ export async function bunInstall() {
4
+ log("🏗️ Running bun install...")
5
+ const proc = Bun.spawn([
6
+ "bun",
7
+ "install",
8
+ "--frozen-lockfile",
9
+ "--cache-dir=.bun-cache",
10
+ ])
11
+ await proc.exited
12
+ const bunInstallText = await Bun.readableStreamToText(proc.stdout)
13
+ if (proc.exitCode === 0) return { error: null }
14
+ return {
15
+ error: {
16
+ signalCode: proc.signalCode,
17
+ exitCode: proc.exitCode,
18
+ text: bunInstallText,
19
+ },
20
+ }
21
+ }
@@ -0,0 +1,14 @@
1
+ export async function getSpawnText(...args: Parameters<typeof Bun.spawn>) {
2
+ const { exitCode, exited, stderr, stdout } = Bun.spawn(...args)
3
+ await exited
4
+ if (exitCode === 0 || exitCode === null)
5
+ return await Bun.readableStreamToText(stdout)
6
+ if (!exitCode) return ""
7
+ throw new Error(
8
+ `Failed to run command: "${args.join(
9
+ " ",
10
+ )}". Exit code: ${exitCode}. Stderr: ${
11
+ stderr ? await Bun.readableStreamToText(stderr) : "None"
12
+ }`,
13
+ )
14
+ }
@@ -0,0 +1,31 @@
1
+ import { log } from "@typed-assistant/logger"
2
+ import { getSpawnText } from "./getSpawnText"
3
+ import { bunInstall } from "./bunInstall"
4
+
5
+ export const pullChanges = async () => {
6
+ if (
7
+ !process.env.GITHUB_TOKEN ||
8
+ !process.env.GITHUB_USERNAME ||
9
+ !process.env.GITHUB_REPO
10
+ ) {
11
+ log(
12
+ "⚠️ Cannot pull changes without GITHUB_TOKEN, GITHUB_USERNAME, and GITHUB_REPO environment variables.",
13
+ )
14
+ return
15
+ }
16
+ log("⬇️ Pulling changes...")
17
+ const gitPullText = await getSpawnText(["git", "pull"])
18
+ const packageJSONUpdated = /package.json/.test(gitPullText)
19
+ const nothingNew = /Already up to date./.test(gitPullText)
20
+ if (nothingNew) {
21
+ log(" 👌 No new changes.")
22
+ return
23
+ } else {
24
+ log(" 👍 Changes pulled.")
25
+ }
26
+ if (packageJSONUpdated) {
27
+ log(" 📦 package.json updated.")
28
+ const { error } = await bunInstall()
29
+ if (error) throw new Error(error.text)
30
+ }
31
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@typed-assistant/typescript-config/react-app.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
package/turbo.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "extends": ["//"],
4
+ "pipeline": {
5
+ "build": {
6
+ "outputs": ["dist/**"]
7
+ }
8
+ }
9
+ }