react-hcl 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # react-hcl
2
2
 
3
- A transpiler that converts TSX into Terraform `.tf` files. Write Terraform configurations using JSX/TSX syntax with a custom JSX runtime (no React dependency).
3
+ A transpiler that converts JSX/TSX into Terraform `.tf` files. Write Terraform configurations using JSX/TSX syntax with a custom JSX runtime (no React dependency).
4
4
 
5
5
  ## Why React for IaC?
6
6
 
@@ -263,4 +263,4 @@ bun run build
263
263
  ## Documentation
264
264
 
265
265
  - [Design Document](docs/design-doc.md)
266
- - [Implementation Plan](docs/plan.md)
266
+ - [Product Requirements](docs/prd.md)
package/dist/cli.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * CLI entry point for react-hcl.
3
3
  *
4
- * Usage: bun src/cli.ts <input.tsx> [-o <file>]
4
+ * Usage: bun src/cli.ts <input.(j|t)sx|-> [-o <file>]
5
+ * cat input.jsx | bun src/cli.ts [-o <file>]
5
6
  *
6
7
  * Pipeline:
7
- * 1. Takes a user-authored .tsx file as input
8
+ * 1. Takes a user-authored JSX/TSX file as input
8
9
  * 2. Transpiles and bundles it with esbuild (JSX → custom runtime calls)
9
10
  * 3. Writes the bundled ESM code to a temp file and dynamically imports it
10
11
  * 4. render() evaluates the JSXElement tree into Block[] IR
package/dist/cli.js CHANGED
@@ -31,6 +31,150 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  mod
32
32
  ));
33
33
 
34
+ // node_modules/arg/index.js
35
+ var require_arg = __commonJS({
36
+ "node_modules/arg/index.js"(exports, module) {
37
+ var flagSymbol = /* @__PURE__ */ Symbol("arg flag");
38
+ var ArgError = class _ArgError extends Error {
39
+ constructor(msg, code) {
40
+ super(msg);
41
+ this.name = "ArgError";
42
+ this.code = code;
43
+ Object.setPrototypeOf(this, _ArgError.prototype);
44
+ }
45
+ };
46
+ function arg(opts, {
47
+ argv = process.argv.slice(2),
48
+ permissive = false,
49
+ stopAtPositional = false
50
+ } = {}) {
51
+ if (!opts) {
52
+ throw new ArgError(
53
+ "argument specification object is required",
54
+ "ARG_CONFIG_NO_SPEC"
55
+ );
56
+ }
57
+ const result = { _: [] };
58
+ const aliases = {};
59
+ const handlers = {};
60
+ for (const key of Object.keys(opts)) {
61
+ if (!key) {
62
+ throw new ArgError(
63
+ "argument key cannot be an empty string",
64
+ "ARG_CONFIG_EMPTY_KEY"
65
+ );
66
+ }
67
+ if (key[0] !== "-") {
68
+ throw new ArgError(
69
+ `argument key must start with '-' but found: '${key}'`,
70
+ "ARG_CONFIG_NONOPT_KEY"
71
+ );
72
+ }
73
+ if (key.length === 1) {
74
+ throw new ArgError(
75
+ `argument key must have a name; singular '-' keys are not allowed: ${key}`,
76
+ "ARG_CONFIG_NONAME_KEY"
77
+ );
78
+ }
79
+ if (typeof opts[key] === "string") {
80
+ aliases[key] = opts[key];
81
+ continue;
82
+ }
83
+ let type = opts[key];
84
+ let isFlag = false;
85
+ if (Array.isArray(type) && type.length === 1 && typeof type[0] === "function") {
86
+ const [fn] = type;
87
+ type = (value, name, prev = []) => {
88
+ prev.push(fn(value, name, prev[prev.length - 1]));
89
+ return prev;
90
+ };
91
+ isFlag = fn === Boolean || fn[flagSymbol] === true;
92
+ } else if (typeof type === "function") {
93
+ isFlag = type === Boolean || type[flagSymbol] === true;
94
+ } else {
95
+ throw new ArgError(
96
+ `type missing or not a function or valid array type: ${key}`,
97
+ "ARG_CONFIG_VAD_TYPE"
98
+ );
99
+ }
100
+ if (key[1] !== "-" && key.length > 2) {
101
+ throw new ArgError(
102
+ `short argument keys (with a single hyphen) must have only one character: ${key}`,
103
+ "ARG_CONFIG_SHORTOPT_TOOLONG"
104
+ );
105
+ }
106
+ handlers[key] = [type, isFlag];
107
+ }
108
+ for (let i = 0, len = argv.length; i < len; i++) {
109
+ const wholeArg = argv[i];
110
+ if (stopAtPositional && result._.length > 0) {
111
+ result._ = result._.concat(argv.slice(i));
112
+ break;
113
+ }
114
+ if (wholeArg === "--") {
115
+ result._ = result._.concat(argv.slice(i + 1));
116
+ break;
117
+ }
118
+ if (wholeArg.length > 1 && wholeArg[0] === "-") {
119
+ const separatedArguments = wholeArg[1] === "-" || wholeArg.length === 2 ? [wholeArg] : wholeArg.slice(1).split("").map((a) => `-${a}`);
120
+ for (let j = 0; j < separatedArguments.length; j++) {
121
+ const arg2 = separatedArguments[j];
122
+ const [originalArgName, argStr] = arg2[1] === "-" ? arg2.split(/=(.*)/, 2) : [arg2, void 0];
123
+ let argName = originalArgName;
124
+ while (argName in aliases) {
125
+ argName = aliases[argName];
126
+ }
127
+ if (!(argName in handlers)) {
128
+ if (permissive) {
129
+ result._.push(arg2);
130
+ continue;
131
+ } else {
132
+ throw new ArgError(
133
+ `unknown or unexpected option: ${originalArgName}`,
134
+ "ARG_UNKNOWN_OPTION"
135
+ );
136
+ }
137
+ }
138
+ const [type, isFlag] = handlers[argName];
139
+ if (!isFlag && j + 1 < separatedArguments.length) {
140
+ throw new ArgError(
141
+ `option requires argument (but was followed by another short argument): ${originalArgName}`,
142
+ "ARG_MISSING_REQUIRED_SHORTARG"
143
+ );
144
+ }
145
+ if (isFlag) {
146
+ result[argName] = type(true, argName, result[argName]);
147
+ } else if (argStr === void 0) {
148
+ if (argv.length < i + 2 || argv[i + 1].length > 1 && argv[i + 1][0] === "-" && !(argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/) && (type === Number || // eslint-disable-next-line no-undef
149
+ typeof BigInt !== "undefined" && type === BigInt))) {
150
+ const extended = originalArgName === argName ? "" : ` (alias for ${argName})`;
151
+ throw new ArgError(
152
+ `option requires argument: ${originalArgName}${extended}`,
153
+ "ARG_MISSING_REQUIRED_LONGARG"
154
+ );
155
+ }
156
+ result[argName] = type(argv[i + 1], argName, result[argName]);
157
+ ++i;
158
+ } else {
159
+ result[argName] = type(argStr, argName, result[argName]);
160
+ }
161
+ }
162
+ } else {
163
+ result._.push(wholeArg);
164
+ }
165
+ }
166
+ return result;
167
+ }
168
+ arg.flag = (fn) => {
169
+ fn[flagSymbol] = true;
170
+ return fn;
171
+ };
172
+ arg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1);
173
+ arg.ArgError = ArgError;
174
+ module.exports = arg;
175
+ }
176
+ });
177
+
34
178
  // node_modules/hcl2-parser/dist/index.js
35
179
  var require_dist = __commonJS({
36
180
  "node_modules/hcl2-parser/dist/index.js"(exports, module) {
@@ -210584,6 +210728,8 @@ var require_dist = __commonJS({
210584
210728
  });
210585
210729
 
210586
210730
  // src/cli.ts
210731
+ var import_arg = __toESM(require_arg(), 1);
210732
+ import { existsSync } from "node:fs";
210587
210733
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
210588
210734
  import { tmpdir } from "node:os";
210589
210735
  import { dirname, join, resolve } from "node:path";
@@ -210910,28 +211056,81 @@ function render(element) {
210910
211056
  // src/cli.ts
210911
211057
  var __filename = fileURLToPath(import.meta.url);
210912
211058
  var __dirname = dirname(__filename);
210913
- function parseArgs(argv) {
210914
- const args = argv.slice(2);
210915
- let inputFile;
210916
- let output;
210917
- for (let i = 0; i < args.length; i++) {
210918
- if ((args[i] === "-o" || args[i] === "--output") && i + 1 < args.length) {
210919
- output = args[++i];
210920
- } else if (!inputFile) {
210921
- inputFile = args[i];
211059
+ function resolvePackageEntrypoint(candidates) {
211060
+ for (const candidate of candidates) {
211061
+ if (existsSync(candidate)) {
211062
+ return candidate;
210922
211063
  }
210923
211064
  }
210924
- if (!inputFile) {
210925
- console.error("Usage: react-hcl <input.tsx> [-o <file>]");
210926
- process.exit(1);
211065
+ return candidates[0];
211066
+ }
211067
+ function parseArgs(argv) {
211068
+ const args = (0, import_arg.default)(
211069
+ {
211070
+ "--help": Boolean,
211071
+ "--output": String,
211072
+ "-h": "--help",
211073
+ "-o": "--output"
211074
+ },
211075
+ { argv: argv.slice(2), permissive: true }
211076
+ );
211077
+ return {
211078
+ inputFile: args._[0] ?? "",
211079
+ output: args["--output"],
211080
+ help: Boolean(args["--help"])
211081
+ };
211082
+ }
211083
+ function printUsage() {
211084
+ console.error("Usage: react-hcl <input.(j|t)sx|-> [-o <file>]");
211085
+ }
211086
+ function printHelp() {
211087
+ process.stdout.write(
211088
+ [
211089
+ "react-hcl - Convert JSX/TSX to Terraform HCL",
211090
+ "",
211091
+ "Usage:",
211092
+ " react-hcl <input.(j|t)sx|-> [-o <file>]",
211093
+ " cat input.jsx | react-hcl [-o <file>]",
211094
+ "",
211095
+ "Options:",
211096
+ " -o, --output <file> Write output to file instead of stdout",
211097
+ " -h, --help Show help",
211098
+ ""
211099
+ ].join("\n")
211100
+ );
211101
+ }
211102
+ async function readStdin() {
211103
+ const chunks = [];
211104
+ for await (const chunk of process.stdin) {
211105
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
210927
211106
  }
210928
- return { inputFile, output };
211107
+ return Buffer.concat(chunks).toString("utf8");
210929
211108
  }
210930
211109
  async function main() {
210931
- const { inputFile, output } = parseArgs(process.argv);
210932
- const absoluteInput = resolve(inputFile);
210933
- const result = await esbuild.build({
210934
- entryPoints: [absoluteInput],
211110
+ let parsedArgs;
211111
+ try {
211112
+ parsedArgs = parseArgs(process.argv);
211113
+ } catch (err) {
211114
+ const message = err instanceof Error ? err.message : String(err);
211115
+ console.error(message);
211116
+ printUsage();
211117
+ process.exit(1);
211118
+ }
211119
+ const { inputFile, output, help } = parsedArgs;
211120
+ if (help) {
211121
+ printHelp();
211122
+ return;
211123
+ }
211124
+ const stdinContents = process.stdin.isTTY ? "" : await readStdin();
211125
+ const hasStdin = stdinContents.trim().length > 0;
211126
+ const wantsStdin = inputFile === "-" || !inputFile && hasStdin;
211127
+ const hasFileInput = Boolean(inputFile && inputFile !== "-");
211128
+ if (hasFileInput && hasStdin) {
211129
+ console.error("Cannot use stdin and input file together.");
211130
+ printUsage();
211131
+ process.exit(1);
211132
+ }
211133
+ const buildBaseOptions = {
210935
211134
  bundle: true,
210936
211135
  format: "esm",
210937
211136
  platform: "node",
@@ -210939,11 +211138,46 @@ async function main() {
210939
211138
  jsxImportSource: "react-hcl",
210940
211139
  write: false,
210941
211140
  alias: {
210942
- "react-hcl/jsx-runtime": resolve(__dirname, "../src/jsx-runtime.ts"),
210943
- "react-hcl": resolve(__dirname, "../src/index.ts")
211141
+ "react-hcl/jsx-runtime": resolvePackageEntrypoint([
211142
+ resolve(__dirname, "jsx-runtime.js"),
211143
+ resolve(__dirname, "../src/jsx-runtime.ts")
211144
+ ]),
211145
+ "react-hcl": resolvePackageEntrypoint([
211146
+ resolve(__dirname, "index.js"),
211147
+ resolve(__dirname, "../src/index.ts")
211148
+ ])
211149
+ }
211150
+ };
211151
+ let result;
211152
+ if (wantsStdin) {
211153
+ if (!hasStdin) {
211154
+ printUsage();
211155
+ process.exit(1);
210944
211156
  }
210945
- });
210946
- const code = result.outputFiles[0].text;
211157
+ result = await esbuild.build({
211158
+ ...buildBaseOptions,
211159
+ stdin: {
211160
+ contents: stdinContents,
211161
+ loader: "tsx",
211162
+ resolveDir: process.cwd(),
211163
+ sourcefile: "stdin.tsx"
211164
+ }
211165
+ });
211166
+ } else if (inputFile) {
211167
+ const absoluteInput = resolve(inputFile);
211168
+ result = await esbuild.build({
211169
+ ...buildBaseOptions,
211170
+ entryPoints: [absoluteInput]
211171
+ });
211172
+ } else {
211173
+ printUsage();
211174
+ process.exit(1);
211175
+ }
211176
+ const outputFile = result.outputFiles?.[0];
211177
+ if (!outputFile) {
211178
+ throw new Error("esbuild did not produce any output file.");
211179
+ }
211180
+ const code = outputFile.text;
210947
211181
  const tmpDir = await mkdtemp(join(tmpdir(), "react-hcl-"));
210948
211182
  const tmpFile = join(tmpDir, "bundle.mjs");
210949
211183
  try {
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Public API entry point for the react-hcl package.
3
3
  *
4
4
  * This module re-exports all user-facing components and utilities.
5
- * Users import from "react-hcl" in their TSX files:
5
+ * Users import from "react-hcl" in their JSX/TSX files:
6
6
  *
7
7
  * import { Resource, Fragment } from "react-hcl";
8
8
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-hcl",
3
- "version": "0.1.0",
4
- "description": "TSX to Terraform (.tf) transpiler with a custom JSX runtime",
3
+ "version": "0.1.1",
4
+ "description": "JSX/TSX to Terraform (.tf) transpiler with a custom JSX runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -41,6 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@biomejs/biome": "^2.3.15",
43
43
  "@types/bun": "^1.3.9",
44
+ "arg": "^5.0.2",
44
45
  "hcl2-parser": "^1.0.3"
45
46
  },
46
47
  "peerDependencies": {