@highstate/cli 0.7.3 → 0.7.6

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/dist/main.js CHANGED
@@ -10,14 +10,14 @@ import pino, { levels } from 'pino';
10
10
  import { readFile, writeFile } from 'node:fs/promises';
11
11
  import { parseAsync } from 'oxc-parser';
12
12
  import { walk } from 'oxc-walker';
13
- import { resolve, relative, dirname } from 'node:path';
14
- import { fileURLToPath } from 'node:url';
13
+ import { resolve, dirname, relative } from 'node:path';
14
+ import { pathToFileURL, fileURLToPath } from 'node:url';
15
15
  import { sha256 } from 'crypto-hash';
16
16
  import { resolve as resolve$1 } from 'import-meta-resolve';
17
17
  import { pipe, mapValues, mapKeys } from 'remeda';
18
18
  import { build } from 'tsup';
19
19
 
20
- var version = "0.7.2";
20
+ var version = "0.7.5";
21
21
 
22
22
  const logger = pino(
23
23
  {
@@ -1185,7 +1185,8 @@ function cleanJsdoc(str) {
1185
1185
  }
1186
1186
 
1187
1187
  class SourceHashCalculator {
1188
- constructor(packageJson, logger) {
1188
+ constructor(packageJsonPath, packageJson, logger) {
1189
+ this.packageJsonPath = packageJsonPath;
1189
1190
  this.packageJson = packageJson;
1190
1191
  this.logger = logger;
1191
1192
  }
@@ -1247,7 +1248,8 @@ class SourceHashCalculator {
1247
1248
  case "npm": {
1248
1249
  let resolvedUrl;
1249
1250
  try {
1250
- resolvedUrl = resolve$1(dependency.package, import.meta.url);
1251
+ const baseUrl = pathToFileURL(dirname(this.packageJsonPath));
1252
+ resolvedUrl = resolve$1(dependency.package, baseUrl.toString());
1251
1253
  } catch (error) {
1252
1254
  this.logger.error(`failed to resolve package "%s"`, dependency.package);
1253
1255
  throw error;
@@ -1329,6 +1331,25 @@ class SourceHashCalculator {
1329
1331
  }
1330
1332
  }
1331
1333
 
1334
+ function createBinTransformerPlugin(sourceFilePaths) {
1335
+ const filter = new RegExp(`(${sourceFilePaths.join("|")})$`);
1336
+ logger.debug("created bin transformer plugin with filter: %s", filter);
1337
+ return {
1338
+ name: "bin-transformer",
1339
+ setup(build) {
1340
+ build.onLoad({ filter }, async (args) => {
1341
+ const content = await readFile(args.path, "utf-8");
1342
+ return {
1343
+ contents: `#!/usr/bin/env node
1344
+
1345
+ ${content}`,
1346
+ loader: "ts"
1347
+ };
1348
+ });
1349
+ }
1350
+ };
1351
+ }
1352
+
1332
1353
  class DesignerCommand extends Command {
1333
1354
  static paths = [["designer"]];
1334
1355
  static usage = Command.Usage({
@@ -1391,16 +1412,26 @@ class BuildCommand extends Command {
1391
1412
  async execute() {
1392
1413
  const packageJson = await readPackageJSON();
1393
1414
  const exports = packageJson.exports;
1394
- if (!exports) {
1395
- logger.warn("no exports found in package.json");
1415
+ let bin = packageJson.bin;
1416
+ if (!packageJson.name) {
1417
+ throw new Error("package.json must have a name field");
1418
+ }
1419
+ if (!exports && !bin) {
1420
+ logger.warn("no exports or bin found in package.json");
1396
1421
  return;
1397
1422
  }
1398
- if (typeof exports !== "object" || Array.isArray(exports)) {
1423
+ if (exports !== void 0 && (typeof exports !== "object" || Array.isArray(exports))) {
1399
1424
  throw new Error("Exports field in package.json must be an object");
1400
1425
  }
1426
+ if (bin !== void 0 && typeof bin !== "object") {
1427
+ bin = { [packageJson.name]: bin };
1428
+ }
1401
1429
  const entry = pipe(
1402
- exports,
1403
- mapValues((value, key) => {
1430
+ {
1431
+ ...mapValues(exports ?? {}, (value) => ({ value, isBin: false })),
1432
+ ...mapValues(bin ?? {}, (value) => ({ value, isBin: true }))
1433
+ },
1434
+ mapValues(({ value, isBin }, key) => {
1404
1435
  let distPath;
1405
1436
  if (typeof value === "string") {
1406
1437
  distPath = value;
@@ -1429,11 +1460,20 @@ class BuildCommand extends Command {
1429
1460
  return {
1430
1461
  entryPoint: `./src/${targetName}.ts`,
1431
1462
  targetName,
1432
- distPath
1463
+ distPath,
1464
+ isBin
1433
1465
  };
1434
1466
  }),
1435
1467
  mapKeys((_, value) => value.targetName)
1436
1468
  );
1469
+ const esbuildPlugins = [];
1470
+ const binSourceFilePaths = Object.values(entry).filter((value) => value.isBin).map((value) => value.entryPoint.slice(2));
1471
+ if (this.library) {
1472
+ esbuildPlugins.push(schemaTransformerPlugin);
1473
+ }
1474
+ if (binSourceFilePaths.length > 0) {
1475
+ esbuildPlugins.push(createBinTransformerPlugin(binSourceFilePaths));
1476
+ }
1437
1477
  await build({
1438
1478
  entry: mapValues(entry, (value) => value.entryPoint),
1439
1479
  outDir: "dist",
@@ -1443,10 +1483,16 @@ class BuildCommand extends Command {
1443
1483
  format: "esm",
1444
1484
  target: "esnext",
1445
1485
  external: ["@pulumi/pulumi"],
1446
- esbuildPlugins: this.library ? [schemaTransformerPlugin] : []
1486
+ esbuildPlugins,
1487
+ silent: ["warn", "error", "fatal"].includes(logger.level)
1447
1488
  });
1489
+ const packageJsonPath = await resolvePackageJSON();
1448
1490
  const upToDatePackageJson = await readPackageJSON();
1449
- const sourceHashCalculator = new SourceHashCalculator(upToDatePackageJson, logger);
1491
+ const sourceHashCalculator = new SourceHashCalculator(
1492
+ packageJsonPath,
1493
+ upToDatePackageJson,
1494
+ logger
1495
+ );
1450
1496
  const distPaths = Object.values(entry).map((value) => value.distPath);
1451
1497
  await sourceHashCalculator.writeHighstateManifest("./dist", distPaths);
1452
1498
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/cli",
3
- "version": "0.7.3",
3
+ "version": "0.7.6",
4
4
  "description": "The CLI for the Highstate project.",
5
5
  "type": "module",
6
6
  "files": [
@@ -18,6 +18,7 @@
18
18
  "build": "pkgroll --clean --tsconfig=tsconfig.build.json"
19
19
  },
20
20
  "dependencies": {
21
+ "@highstate/backend": "^0.7.6",
21
22
  "clipanion": "^4.0.0-rc.4",
22
23
  "consola": "^3.4.0",
23
24
  "crypto-hash": "^3.1.0",
@@ -32,10 +33,8 @@
32
33
  "tsup": "^8.4.0"
33
34
  },
34
35
  "devDependencies": {
36
+ "highstate-cli-bootstrap": "npm:@highstate/cli@0.7.4",
35
37
  "pkgroll": "^2.5.1"
36
38
  },
37
- "peerDependencies": {
38
- "@highstate/backend": "workspace:^0.7.2"
39
- },
40
- "gitHead": "5cf7cec27262c8fa1d96f6478833b94841459d64"
39
+ "gitHead": "5acce7f9d8a57bdd1220e381d5b76d7b484b98c2"
41
40
  }
@@ -1,8 +1,14 @@
1
+ import type { Plugin } from "esbuild"
1
2
  import { Command, Option } from "clipanion"
2
- import { readPackageJSON } from "pkg-types"
3
+ import { readPackageJSON, resolvePackageJSON } from "pkg-types"
3
4
  import { mapKeys, mapValues, pipe } from "remeda"
4
5
  import { build } from "tsup"
5
- import { logger, schemaTransformerPlugin, SourceHashCalculator } from "../shared"
6
+ import {
7
+ createBinTransformerPlugin,
8
+ logger,
9
+ schemaTransformerPlugin,
10
+ SourceHashCalculator,
11
+ } from "../shared"
6
12
 
7
13
  export class BuildCommand extends Command {
8
14
  static paths = [["build"]]
@@ -18,19 +24,31 @@ export class BuildCommand extends Command {
18
24
  async execute(): Promise<void> {
19
25
  const packageJson = await readPackageJSON()
20
26
  const exports = packageJson.exports
27
+ let bin = packageJson.bin
21
28
 
22
- if (!exports) {
23
- logger.warn("no exports found in package.json")
29
+ if (!packageJson.name) {
30
+ throw new Error("package.json must have a name field")
31
+ }
32
+
33
+ if (!exports && !bin) {
34
+ logger.warn("no exports or bin found in package.json")
24
35
  return
25
36
  }
26
37
 
27
- if (typeof exports !== "object" || Array.isArray(exports)) {
38
+ if (exports !== undefined && (typeof exports !== "object" || Array.isArray(exports))) {
28
39
  throw new Error("Exports field in package.json must be an object")
29
40
  }
30
41
 
42
+ if (bin !== undefined && typeof bin !== "object") {
43
+ bin = { [packageJson.name]: bin }
44
+ }
45
+
31
46
  const entry = pipe(
32
- exports,
33
- mapValues((value, key) => {
47
+ {
48
+ ...mapValues(exports ?? {}, value => ({ value, isBin: false })),
49
+ ...mapValues(bin ?? {}, value => ({ value, isBin: true })),
50
+ },
51
+ mapValues(({ value, isBin }, key) => {
34
52
  let distPath
35
53
 
36
54
  if (typeof value === "string") {
@@ -67,11 +85,26 @@ export class BuildCommand extends Command {
67
85
  entryPoint: `./src/${targetName}.ts`,
68
86
  targetName,
69
87
  distPath,
88
+ isBin,
70
89
  }
71
90
  }),
72
91
  mapKeys((_, value) => value.targetName),
73
92
  )
74
93
 
94
+ const esbuildPlugins: Plugin[] = []
95
+
96
+ const binSourceFilePaths = Object.values(entry)
97
+ .filter(value => value.isBin)
98
+ .map(value => value.entryPoint.slice(2)) // remove "./"
99
+
100
+ if (this.library) {
101
+ esbuildPlugins.push(schemaTransformerPlugin)
102
+ }
103
+
104
+ if (binSourceFilePaths.length > 0) {
105
+ esbuildPlugins.push(createBinTransformerPlugin(binSourceFilePaths))
106
+ }
107
+
75
108
  await build({
76
109
  entry: mapValues(entry, value => value.entryPoint),
77
110
  outDir: "dist",
@@ -81,12 +114,18 @@ export class BuildCommand extends Command {
81
114
  format: "esm",
82
115
  target: "esnext",
83
116
  external: ["@pulumi/pulumi"],
84
- esbuildPlugins: this.library ? [schemaTransformerPlugin] : [],
117
+ esbuildPlugins,
118
+ silent: ["warn", "error", "fatal"].includes(logger.level),
85
119
  })
86
120
 
121
+ const packageJsonPath = await resolvePackageJSON()
87
122
  const upToDatePackageJson = await readPackageJSON()
88
123
 
89
- const sourceHashCalculator = new SourceHashCalculator(upToDatePackageJson, logger)
124
+ const sourceHashCalculator = new SourceHashCalculator(
125
+ packageJsonPath,
126
+ upToDatePackageJson,
127
+ logger,
128
+ )
90
129
  const distPaths = Object.values(entry).map(value => value.distPath)
91
130
 
92
131
  await sourceHashCalculator.writeHighstateManifest("./dist", distPaths)
@@ -0,0 +1,23 @@
1
+ import type { Plugin } from "esbuild"
2
+ import { readFile } from "node:fs/promises"
3
+ import { logger } from "./logger"
4
+
5
+ export function createBinTransformerPlugin(sourceFilePaths: string[]): Plugin {
6
+ const filter = new RegExp(`(${sourceFilePaths.join("|")})$`)
7
+
8
+ logger.debug("created bin transformer plugin with filter: %s", filter)
9
+
10
+ return {
11
+ name: "bin-transformer",
12
+ setup(build) {
13
+ build.onLoad({ filter }, async args => {
14
+ const content = await readFile(args.path, "utf-8")
15
+
16
+ return {
17
+ contents: `#!/usr/bin/env node\n\n${content}`,
18
+ loader: "ts",
19
+ }
20
+ })
21
+ },
22
+ }
23
+ }
@@ -2,3 +2,4 @@ export * from "./services"
2
2
  export * from "./logger"
3
3
  export * from "./schema-transformer"
4
4
  export * from "./source-hash-calculator"
5
+ export * from "./bin-transformer"
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from "pino"
2
2
  import { dirname, relative, resolve } from "node:path"
3
3
  import { readFile, writeFile } from "node:fs/promises"
4
- import { fileURLToPath } from "node:url"
4
+ import { fileURLToPath, pathToFileURL } from "node:url"
5
5
  import { readPackageJSON, resolvePackageJSON, type PackageJson } from "pkg-types"
6
6
  import { sha256 } from "crypto-hash"
7
7
  import { resolve as importMetaResolve } from "import-meta-resolve"
@@ -27,6 +27,7 @@ export class SourceHashCalculator {
27
27
  private readonly fileHashes = new Map<string, Promise<string>>()
28
28
 
29
29
  constructor(
30
+ private readonly packageJsonPath: string,
30
31
  private readonly packageJson: PackageJson,
31
32
  private readonly logger: Logger,
32
33
  ) {}
@@ -102,7 +103,9 @@ export class SourceHashCalculator {
102
103
  case "npm": {
103
104
  let resolvedUrl
104
105
  try {
105
- resolvedUrl = importMetaResolve(dependency.package, import.meta.url)
106
+ const baseUrl = pathToFileURL(dirname(this.packageJsonPath))
107
+
108
+ resolvedUrl = importMetaResolve(dependency.package, baseUrl.toString())
106
109
  } catch (error) {
107
110
  this.logger.error(`failed to resolve package "%s"`, dependency.package)
108
111
  throw error