@highstate/cli 0.14.0 → 0.15.0
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/assets/template/.yarnrc.tpl.yml +3 -0
- package/assets/template/package.tpl.json +22 -0
- package/assets/template/packages/library/package.tpl.json +14 -0
- package/assets/template/packages/library/src/index.ts +8 -0
- package/assets/template/packages/library/src/my-component.ts +27 -0
- package/assets/template/packages/library/src/my-unit.ts +23 -0
- package/assets/template/packages/library/tsconfig.tpl.json +3 -0
- package/assets/template/packages/units/package.tpl.json +15 -0
- package/assets/template/packages/units/src/my-unit/index.ts +11 -0
- package/assets/template/packages/units/tsconfig.tpl.json +3 -0
- package/assets/template/pnpm-workspace.tpl.yaml +4 -0
- package/assets/template/tsconfig.base.tpl.json +3 -0
- package/dist/highstate.manifest.json +1 -1
- package/dist/main.js +188 -9
- package/dist/main.js.map +1 -1
- package/package.json +9 -3
- package/src/commands/init.ts +189 -0
- package/src/main.ts +2 -0
- package/src/shared/generator.spec.ts +73 -0
- package/src/shared/generator.ts +76 -0
- package/src/shared/index.ts +1 -0
|
@@ -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,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,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
|
+
})
|
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,
|
|
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.
|
|
32
|
+
var version = "0.14.1";
|
|
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(
|
|
493
|
-
return crc32(Buffer.from(
|
|
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((
|
|
1122
|
+
await new Promise((resolve6) => {
|
|
1079
1123
|
console.log = (message) => {
|
|
1080
1124
|
if (message.startsWith("Listening on")) {
|
|
1081
|
-
|
|
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);
|