@highstate/cli 0.14.0 → 0.14.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.
@@ -0,0 +1,3 @@
1
+ {{#if isYarn}}
2
+ nodeLinker: node-modules
3
+ {{/if}}
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "private": true,
4
+ {{#if isYarn}}
5
+ "packageManager": "yarn@4.12.0",
6
+ {{/if}}
7
+ {{#if isPnpm}}
8
+ "packageManager": "pnpm@10.26.1",
9
+ {{/if}}
10
+ {{#unless isPnpm}}
11
+ "workspaces": ["packages/*"],
12
+ {{/unless}}
13
+ "dependencies": {
14
+ "@highstate/contract": "{{platformVersion}}",
15
+ "@highstate/pulumi": "{{platformVersion}}",
16
+ "@highstate/library": "{{libraryVersion}}"
17
+ },
18
+ "devDependencies": {
19
+ "@highstate/cli": "{{platformVersion}}",
20
+ "typescript": "^5.9.3"
21
+ }
22
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@{{projectName}}/library",
3
+ "private": true,
4
+ "exports": {
5
+ ".": {
6
+ "types": "./src/index.ts",
7
+ "default": "./dist/index.js"
8
+ }
9
+ },
10
+ "dependencies": {
11
+ "@highstate/contract": "*",
12
+ "@highstate/library": "*"
13
+ }
14
+ }
@@ -0,0 +1,8 @@
1
+ // In this package you can define your custom components and units.
2
+ // The structure is up to you.
3
+ // See: https://highstate.io/fundamentals/components
4
+ // Also see standard library package for inspiration:
5
+ // https://github.com/highstate-io/highstate/tree/main/packages/standard/library
6
+
7
+ export * from "./my-component"
8
+ export * from "./my-unit"
@@ -0,0 +1,27 @@
1
+ import { defineComponent } from "@highstate/contract"
2
+ import { common } from "@highstate/library"
3
+
4
+ export const myComponent = defineComponent({
5
+ type: "{{projectName}}.my-component.v0",
6
+
7
+ inputs: {
8
+ server: common.serverEntity,
9
+ },
10
+
11
+ outputs: {
12
+ server: common.serverEntity,
13
+ },
14
+
15
+ create({ name, inputs }) {
16
+ const { server } = common.script({
17
+ name,
18
+ inputs,
19
+
20
+ args: {
21
+ script: "echo 'Hello, Highstate!'",
22
+ },
23
+ })
24
+
25
+ return { server }
26
+ },
27
+ })
@@ -0,0 +1,23 @@
1
+ import { defineUnit, z } from "@highstate/contract"
2
+ import { common } from "@highstate/library"
3
+
4
+ export const myUnit = defineUnit({
5
+ type: "{{projectName}}.my-unit.v0",
6
+
7
+ args: {
8
+ message: z.string().default("Hello from my-unit!"),
9
+ },
10
+
11
+ inputs: {
12
+ server: common.serverEntity,
13
+ },
14
+
15
+ outputs: {
16
+ server: common.serverEntity,
17
+ },
18
+
19
+ source: {
20
+ package: "@{{projectName}}/units",
21
+ path: "my-unit",
22
+ },
23
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@{{projectName}}/units",
3
+ "private": true,
4
+ "dependencies": {
5
+ "@{{projectName}}/library": "workspace:*"
6
+ },
7
+ "exports":{
8
+ "./my-unit": "./dist/my-unit/index.js"
9
+ },
10
+ "peerDependencies": {
11
+ "@highstate/contract": "*",
12
+ "@highstate/pulumi": "*",
13
+ "@pulumi/pulumi": "*"
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ import { myUnit } from "@{{projectName}}/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+
4
+ const { inputs, outputs } = forUnit(myUnit)
5
+
6
+ // In this program you can implement the logic of your custom unit using Pulumi SDK.
7
+ // See: https://highstate.io/fundamentals/units
8
+
9
+ export default outputs({
10
+ server: inputs.server,
11
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ }
@@ -0,0 +1,4 @@
1
+ {{#if isPnpm}}
2
+ packages:
3
+ - "packages/*"
4
+ {{/if}}
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./node_modules/@highstate/cli/assets/tsconfig.base.json",
3
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "sourceHashes": {
3
- "./dist/main.js": 3586364367
3
+ "./dist/main.js": 4001946629
4
4
  }
5
5
  }
package/dist/main.js CHANGED
@@ -4,31 +4,32 @@ import { Command, Option, UsageError, Cli, Builtins } from 'clipanion';
4
4
  import { hostname } from 'node:os';
5
5
  import { loadConfig } from '@highstate/backend';
6
6
  import { identityToRecipient } from 'age-encryption';
7
- import { writeFile, rm, readFile, mkdir, readdir } from 'node:fs/promises';
7
+ import { writeFile, mkdir, access, readdir, rm, readFile, stat } from 'node:fs/promises';
8
8
  import { PassThrough } from 'node:stream';
9
9
  import { LogLevels, consola } from 'consola';
10
10
  import pino, { levels } from 'pino';
11
+ import { resolve, basename, relative, dirname, join } from 'node:path';
12
+ import Handlebars from 'handlebars';
11
13
  import MagicString from 'magic-string';
12
14
  import { parseAsync } from 'oxc-parser';
13
15
  import { walk } from 'oxc-walker';
14
16
  import { z } from 'zod';
15
- import { resolve, relative, dirname, join } from 'node:path';
16
17
  import { pathToFileURL, fileURLToPath } from 'node:url';
17
18
  import { crc32 } from '@aws-crypto/crc32';
18
19
  import { resolve as resolve$1 } from 'import-meta-resolve';
19
20
  import { readPackageJSON, resolvePackageJSON } from 'pkg-types';
20
21
  import { existsSync } from 'node:fs';
21
- import { input, confirm } from '@inquirer/prompts';
22
+ import { input, confirm, select } from '@inquirer/prompts';
22
23
  import { Table } from 'console-table-printer';
23
24
  import { encode } from '@msgpack/msgpack';
24
25
  import { mapValues } from 'remeda';
25
26
  import { build } from 'tsup';
26
27
  import { colorize } from 'consola/utils';
27
28
  import { getPort } from 'get-port-please';
28
- import { addDevDependency } from 'nypm';
29
+ import { addDevDependency, installDependencies } from 'nypm';
29
30
 
30
31
  // package.json
31
- var version = "0.13.2";
32
+ var version = "0.14.0";
32
33
  var logger = pino(
33
34
  {
34
35
  name: "highstate-cli",
@@ -172,6 +173,49 @@ function extractEntryPoints(packageJson) {
172
173
  }
173
174
  return result;
174
175
  }
176
+ async function generateFromTemplate(templatePath, destinationPath, variables) {
177
+ const resolvedTemplatePath = resolve(templatePath);
178
+ const resolvedDestinationPath = resolve(destinationPath);
179
+ const templateStats = await stat(resolvedTemplatePath);
180
+ if (!templateStats.isDirectory()) {
181
+ throw new Error(`templatePath must be a directory: ${resolvedTemplatePath}`);
182
+ }
183
+ await mkdir(resolvedDestinationPath, { recursive: true });
184
+ const renderTemplate = (raw) => {
185
+ const template = Handlebars.compile(raw, {
186
+ strict: true,
187
+ noEscape: true
188
+ });
189
+ return template(variables);
190
+ };
191
+ const visit = async (absoluteSourcePath) => {
192
+ const sourceStats = await stat(absoluteSourcePath);
193
+ if (sourceStats.isDirectory()) {
194
+ const relativePath = relative(resolvedTemplatePath, absoluteSourcePath);
195
+ const destinationDirPath = join(resolvedDestinationPath, relativePath);
196
+ await mkdir(destinationDirPath, { recursive: true });
197
+ const entries = await readdir(absoluteSourcePath, { withFileTypes: true });
198
+ for (const entry of entries) {
199
+ await visit(join(absoluteSourcePath, entry.name));
200
+ }
201
+ return;
202
+ }
203
+ if (!sourceStats.isFile()) {
204
+ return;
205
+ }
206
+ const relativeFilePath = relative(resolvedTemplatePath, absoluteSourcePath);
207
+ const destinationRelativePath = relativeFilePath.replaceAll(".tpl", "");
208
+ const destinationFilePath = join(resolvedDestinationPath, destinationRelativePath);
209
+ await mkdir(dirname(destinationFilePath), { recursive: true });
210
+ const contents = await readFile(absoluteSourcePath, "utf8");
211
+ const rendered = renderTemplate(contents);
212
+ if (rendered.trim().length === 0) {
213
+ return;
214
+ }
215
+ await writeFile(destinationFilePath, rendered, "utf8");
216
+ };
217
+ await visit(resolvedTemplatePath);
218
+ }
175
219
  var schemaTransformerPlugin = {
176
220
  name: "schema-transformer",
177
221
  setup(build2) {
@@ -489,8 +533,8 @@ var SourceHashCalculator = class {
489
533
  /**
490
534
  * Calculates CRC32 hash of a string.
491
535
  */
492
- hashString(input2) {
493
- return crc32(Buffer.from(input2));
536
+ hashString(input3) {
537
+ return crc32(Buffer.from(input3));
494
538
  }
495
539
  /**
496
540
  * Gets the highstate configuration from package.json with defaults.
@@ -1075,10 +1119,10 @@ var DesignerCommand = class extends Command {
1075
1119
  process.env.NITRO_HOST = "0.0.0.0";
1076
1120
  process.env.NUXT_PUBLIC_VERSION = designerPackageJson.version;
1077
1121
  process.env.NUXT_PUBLIC_EVENTS_PORT = eventsPort.toString();
1078
- await new Promise((resolve4) => {
1122
+ await new Promise((resolve6) => {
1079
1123
  console.log = (message) => {
1080
1124
  if (message.startsWith("Listening on")) {
1081
- resolve4();
1125
+ resolve6();
1082
1126
  }
1083
1127
  };
1084
1128
  const serverPath = resolve$1("@highstate/designer/server", packageJsonUrl);
@@ -1102,6 +1146,140 @@ var DesignerCommand = class extends Command {
1102
1146
  });
1103
1147
  }
1104
1148
  };
1149
+ var InitCommand = class _InitCommand extends Command {
1150
+ static paths = [["init"]];
1151
+ static usage = Command.Usage({
1152
+ description: "Initializes a new Highstate project."
1153
+ });
1154
+ pathOption = Option.String("--path,-p", {
1155
+ description: "The path where the project should be initialized."
1156
+ });
1157
+ packageManager = Option.String("--package-manager", {
1158
+ description: "The package manager to use (npm, yarn, pnpm)."
1159
+ });
1160
+ name = Option.String("--name", {
1161
+ description: "The project name."
1162
+ });
1163
+ static defaultPlatformVersion = "0.14.0";
1164
+ static defaultLibraryVersion = "0.14.0";
1165
+ async execute() {
1166
+ const availablePackageManagers = await detectAvailablePackageManagers(["npm", "pnpm", "yarn"]);
1167
+ if (availablePackageManagers.length === 0) {
1168
+ throw new Error("no supported package managers found in PATH (npm, pnpm, yarn)");
1169
+ }
1170
+ const destinationPath = await resolveDestinationPath(this.pathOption);
1171
+ const defaultName = basename(destinationPath);
1172
+ const projectName = await resolveProjectName(this.name, defaultName);
1173
+ const selectedPackageManager = await resolvePackageManager(
1174
+ this.packageManager,
1175
+ availablePackageManagers
1176
+ );
1177
+ const templatePath = resolveTemplatePath();
1178
+ await mkdir(destinationPath, { recursive: true });
1179
+ const isEmptyOrMissing = await isEmptyDirectory(destinationPath);
1180
+ if (!isEmptyOrMissing) {
1181
+ throw new Error(`destination path is not empty: ${destinationPath}`);
1182
+ }
1183
+ logger.info("initializing highstate project in %s", destinationPath);
1184
+ await generateFromTemplate(templatePath, destinationPath, {
1185
+ projectName,
1186
+ packageName: projectName,
1187
+ platformVersion: _InitCommand.defaultPlatformVersion,
1188
+ libraryVersion: _InitCommand.defaultLibraryVersion,
1189
+ isPnpm: selectedPackageManager === "pnpm" ? "true" : "",
1190
+ isYarn: selectedPackageManager === "yarn" ? "true" : ""
1191
+ });
1192
+ logger.info("installing dependencies using %s...", selectedPackageManager);
1193
+ await installDependencies({
1194
+ cwd: destinationPath,
1195
+ packageManager: selectedPackageManager,
1196
+ silent: false
1197
+ });
1198
+ logger.info("project initialized successfully");
1199
+ }
1200
+ };
1201
+ async function resolveDestinationPath(pathOption) {
1202
+ if (pathOption) {
1203
+ return resolve(pathOption);
1204
+ }
1205
+ const pathValue = await input({
1206
+ message: "Project path",
1207
+ default: ".",
1208
+ validate: (value) => value.trim().length > 0 ? true : "Path is required"
1209
+ });
1210
+ return resolve(pathValue);
1211
+ }
1212
+ async function resolveProjectName(nameOption, defaultName) {
1213
+ if (nameOption) {
1214
+ return nameOption.trim();
1215
+ }
1216
+ const value = await input({
1217
+ message: "Project name",
1218
+ default: defaultName,
1219
+ validate: (inputValue) => inputValue.trim().length > 0 ? true : "Name is required"
1220
+ });
1221
+ return value.trim();
1222
+ }
1223
+ async function resolvePackageManager(packageManagerOption, available) {
1224
+ if (packageManagerOption) {
1225
+ if (!isSupportedPackageManagerName(packageManagerOption)) {
1226
+ throw new Error(`unsupported package manager: ${packageManagerOption}`);
1227
+ }
1228
+ const name = packageManagerOption;
1229
+ if (!available.includes(name)) {
1230
+ throw new Error(`package manager not found in PATH: ${name}`);
1231
+ }
1232
+ return name;
1233
+ }
1234
+ const preferredOrder = ["pnpm", "yarn", "npm"];
1235
+ const defaultValue = preferredOrder.find((value) => available.includes(value)) ?? available[0];
1236
+ return await select({
1237
+ message: "Package manager",
1238
+ default: defaultValue,
1239
+ choices: available.map((value) => ({ name: value, value }))
1240
+ });
1241
+ }
1242
+ function isSupportedPackageManagerName(value) {
1243
+ return value === "npm" || value === "pnpm" || value === "yarn";
1244
+ }
1245
+ async function detectAvailablePackageManagers(candidates) {
1246
+ const results = [];
1247
+ for (const candidate of candidates) {
1248
+ const exists = await isExecutableInPath(candidate);
1249
+ if (exists) {
1250
+ results.push(candidate);
1251
+ }
1252
+ }
1253
+ return results;
1254
+ }
1255
+ async function isExecutableInPath(command) {
1256
+ const pathValue = process.env.PATH;
1257
+ if (!pathValue) {
1258
+ return false;
1259
+ }
1260
+ const parts = pathValue.split(":").filter(Boolean);
1261
+ for (const part of parts) {
1262
+ const candidate = resolve(part, command);
1263
+ try {
1264
+ await access(candidate);
1265
+ return true;
1266
+ } catch {
1267
+ }
1268
+ }
1269
+ return false;
1270
+ }
1271
+ async function isEmptyDirectory(path) {
1272
+ try {
1273
+ const entries = await readdir(path);
1274
+ return entries.length === 0;
1275
+ } catch {
1276
+ return true;
1277
+ }
1278
+ }
1279
+ function resolveTemplatePath() {
1280
+ const here = fileURLToPath(new URL(import.meta.url));
1281
+ return resolve(here, "..", "..", "assets", "template");
1282
+ }
1105
1283
  var CreateCommand = class extends Command {
1106
1284
  static paths = [["package", "create"]];
1107
1285
  static usage = Command.Usage({
@@ -1195,6 +1373,7 @@ var cli = new Cli({
1195
1373
  });
1196
1374
  cli.register(BuildCommand);
1197
1375
  cli.register(DesignerCommand);
1376
+ cli.register(InitCommand);
1198
1377
  cli.register(BackendIdentityCommand);
1199
1378
  cli.register(BackendUnlockMethodListCommand);
1200
1379
  cli.register(BackendUnlockMethodAddCommand);