@player-tools/fluent-generator 0.13.0--canary.221.5819
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/cjs/index.cjs +510 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +472 -0
- package/dist/index.mjs +472 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
- package/src/__tests__/__snapshots__/generator.test.ts.snap +875 -0
- package/src/__tests__/generator.test.ts +1142 -0
- package/src/cli.ts +192 -0
- package/src/generator.ts +596 -0
- package/src/index.ts +12 -0
- package/src/utils.ts +209 -0
- package/types/cli.d.ts +6 -0
- package/types/generator.d.ts +77 -0
- package/types/index.d.ts +8 -0
- package/types/utils.d.ts +73 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for fluent-generator
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
existsSync,
|
|
11
|
+
accessSync,
|
|
12
|
+
constants,
|
|
13
|
+
} from "fs";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import type { NamedType, ObjectType } from "@player-tools/xlr";
|
|
16
|
+
import { generateFluentBuilder } from "./generator";
|
|
17
|
+
|
|
18
|
+
interface Manifest {
|
|
19
|
+
version: number;
|
|
20
|
+
capabilities: {
|
|
21
|
+
Assets?: string[];
|
|
22
|
+
Views?: string[];
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface CliArgs {
|
|
27
|
+
input: string;
|
|
28
|
+
output: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseArgs(): CliArgs {
|
|
32
|
+
const args = process.argv.slice(2);
|
|
33
|
+
let input = "";
|
|
34
|
+
let output = "./dist";
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < args.length; i++) {
|
|
37
|
+
const arg = args[i];
|
|
38
|
+
if (arg === "-i" || arg === "--input") {
|
|
39
|
+
input = args[++i] || "";
|
|
40
|
+
} else if (arg === "-o" || arg === "--output") {
|
|
41
|
+
output = args[++i] || "./dist";
|
|
42
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
43
|
+
printHelp();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!input) {
|
|
49
|
+
console.error(
|
|
50
|
+
"Error: Must supply an input directory with `-i` or `--input`",
|
|
51
|
+
);
|
|
52
|
+
printHelp();
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { input, output };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function printHelp(): void {
|
|
60
|
+
console.log(`
|
|
61
|
+
fluent-generator - Generate fluent builders from XLR types
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
fluent-generator -i <input-dir> -o <output-dir>
|
|
65
|
+
|
|
66
|
+
Options:
|
|
67
|
+
-i, --input Directory containing manifest.json and XLR JSON files (required)
|
|
68
|
+
-o, --output Directory to write generated builder files (default: ./dist)
|
|
69
|
+
-h, --help Show this help message
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function loadManifest(inputDir: string): Manifest {
|
|
74
|
+
const manifestPath = join(inputDir, "manifest.json");
|
|
75
|
+
if (!existsSync(manifestPath)) {
|
|
76
|
+
throw new Error(`Manifest not found: ${manifestPath}`);
|
|
77
|
+
}
|
|
78
|
+
const content = readFileSync(manifestPath, "utf-8");
|
|
79
|
+
return JSON.parse(content) as Manifest;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function loadXlrType(
|
|
83
|
+
inputDir: string,
|
|
84
|
+
typeName: string,
|
|
85
|
+
): NamedType<ObjectType> {
|
|
86
|
+
const typePath = join(inputDir, `${typeName}.json`);
|
|
87
|
+
if (!existsSync(typePath)) {
|
|
88
|
+
throw new Error(`XLR type file not found: ${typePath}`);
|
|
89
|
+
}
|
|
90
|
+
const content = readFileSync(typePath, "utf-8");
|
|
91
|
+
return JSON.parse(content) as NamedType<ObjectType>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validates that the output directory can be created and is writable.
|
|
96
|
+
* Creates the directory if it doesn't exist.
|
|
97
|
+
*/
|
|
98
|
+
function validateOutputDirectory(outputDir: string): void {
|
|
99
|
+
if (!existsSync(outputDir)) {
|
|
100
|
+
try {
|
|
101
|
+
mkdirSync(outputDir, { recursive: true });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Cannot create output directory "${outputDir}": ${error instanceof Error ? error.message : String(error)}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
accessSync(outputDir, constants.W_OK);
|
|
111
|
+
} catch {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Output directory "${outputDir}" is not writable. Check permissions.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function writeBuilderFile(
|
|
119
|
+
outputDir: string,
|
|
120
|
+
typeName: string,
|
|
121
|
+
code: string,
|
|
122
|
+
): void {
|
|
123
|
+
const fileName =
|
|
124
|
+
typeName
|
|
125
|
+
.replace(/([A-Z])/g, "-$1")
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
.replace(/^-/, "") + ".builder.ts";
|
|
128
|
+
|
|
129
|
+
const filePath = join(outputDir, fileName);
|
|
130
|
+
writeFileSync(filePath, code, "utf-8");
|
|
131
|
+
console.log(` Generated: ${fileName}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function main(): Promise<void> {
|
|
135
|
+
const { input, output } = parseArgs();
|
|
136
|
+
|
|
137
|
+
console.log("━━━ Fluent Generator ━━━");
|
|
138
|
+
console.log(`Input: ${input}`);
|
|
139
|
+
console.log(`Output: ${output}`);
|
|
140
|
+
console.log();
|
|
141
|
+
|
|
142
|
+
// Validate output directory is writable before processing
|
|
143
|
+
validateOutputDirectory(output);
|
|
144
|
+
|
|
145
|
+
console.log("Loading manifest...");
|
|
146
|
+
const manifest = loadManifest(input);
|
|
147
|
+
console.log(`Manifest version: ${manifest.version}`);
|
|
148
|
+
console.log();
|
|
149
|
+
|
|
150
|
+
const { Assets = [], Views = [] } = manifest.capabilities;
|
|
151
|
+
const allTypes = [...Assets, ...Views];
|
|
152
|
+
|
|
153
|
+
if (allTypes.length === 0) {
|
|
154
|
+
console.log("No types found in manifest capabilities.");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`Processing ${allTypes.length} type(s)...`);
|
|
159
|
+
console.log();
|
|
160
|
+
|
|
161
|
+
let succeeded = 0;
|
|
162
|
+
let failed = 0;
|
|
163
|
+
|
|
164
|
+
for (const typeName of allTypes) {
|
|
165
|
+
try {
|
|
166
|
+
const xlrType = loadXlrType(input, typeName);
|
|
167
|
+
const code = generateFluentBuilder(xlrType);
|
|
168
|
+
writeBuilderFile(output, typeName, code);
|
|
169
|
+
succeeded++;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(
|
|
172
|
+
` Failed to generate ${typeName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
173
|
+
);
|
|
174
|
+
failed++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log();
|
|
179
|
+
console.log("━━━ Summary ━━━");
|
|
180
|
+
console.log(
|
|
181
|
+
`✔ Generation complete: ${succeeded} succeeded, ${failed} failed`,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
if (failed > 0) {
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
main().catch((error) => {
|
|
190
|
+
console.error("Fatal error:", error);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
});
|