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 +2 -2
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +255 -21
- package/dist/index.d.ts +1 -1
- package/package.json +3 -2
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
|
-
- [
|
|
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.
|
|
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
|
|
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
|
|
210914
|
-
const
|
|
210915
|
-
|
|
210916
|
-
|
|
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
|
-
|
|
210925
|
-
|
|
210926
|
-
|
|
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
|
|
211107
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
210929
211108
|
}
|
|
210930
211109
|
async function main() {
|
|
210931
|
-
|
|
210932
|
-
|
|
210933
|
-
|
|
210934
|
-
|
|
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":
|
|
210943
|
-
|
|
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
|
-
|
|
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.
|
|
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": {
|