@vaadin/hilla-generator-cli 24.8.0-alpha1 → 24.8.0-alpha2

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/GeneratorIO.d.ts CHANGED
@@ -1,31 +1,22 @@
1
1
  import { type PluginConstructor } from "@vaadin/hilla-generator-core/Plugin.js";
2
2
  import type LoggerFactory from "@vaadin/hilla-generator-utils/LoggerFactory.js";
3
+ export declare const GENERATED_LIST_FILENAME = "generated-file-list.txt";
3
4
  export default class GeneratorIO {
4
5
  #private;
5
- static readonly INDEX_FILENAME = "generated-file-list.txt";
6
- ["constructor"]: typeof GeneratorIO;
7
- readonly cwd: string;
8
- constructor(outputDir: string, logger: LoggerFactory);
6
+ constructor(outputDir: URL, logger: LoggerFactory);
9
7
  /**
10
- * Gets the list of files generated the last time. The info is found in {@link INDEX_FILENAME}.
8
+ * Gets the list of files generated the last time. The info is found in {@link GENERATED_LIST_FILENAME}.
11
9
  * @returns a list of files that have been generated by us
12
10
  */
13
- getGeneratedFiles(): Promise<Set<string>>;
11
+ getExistingGeneratedFiles(): Promise<readonly string[]>;
14
12
  /**
15
13
  * Cleans the output directory by keeping the generated files and deleting the rest of the given files.
16
14
  *
17
- * @returns a set containing deleted filenames
15
+ * @returns a list with names of deleted files
18
16
  */
19
- cleanOutputDir(generatedFiles: string[], filesToDelete: Set<string>): Promise<Set<string>>;
20
- createFileIndex(filenames: string[]): Promise<void>;
21
- writeGeneratedFiles(files: readonly File[]): Promise<string[]>;
22
- /**
23
- * Checks that a file exists (is visible)
24
- * @param path - the file path to check
25
- */
26
- static exists(path: string): Promise<boolean>;
17
+ cleanOutputDir(generatedFiles: readonly string[], filesToDelete: readonly string[]): Promise<readonly string[]>;
18
+ writeGeneratedFiles(files: readonly File[]): Promise<readonly string[]>;
27
19
  loadPlugin(modulePath: string): Promise<PluginConstructor>;
28
- resolveGeneratedFile(filename: string): string;
29
- read(path: string): Promise<string>;
30
- write(filename: string, content: string): Promise<void>;
20
+ read(filename: string): Promise<string>;
21
+ write(file: File): Promise<void>;
31
22
  }
package/GeneratorIO.js CHANGED
@@ -1,115 +1,88 @@
1
- import { constants } from "node:fs";
2
- import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
- import { createRequire } from "node:module";
4
- import { dirname, isAbsolute, join, resolve } from "node:path";
5
- import { pathToFileURL } from "node:url";
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
6
2
  import Plugin from "@vaadin/hilla-generator-core/Plugin.js";
7
3
  import GeneratorIOException from "./GeneratorIOException.js";
8
- const require = createRequire(import.meta.url);
4
+ export const GENERATED_LIST_FILENAME = "generated-file-list.txt";
9
5
  export default class GeneratorIO {
10
- static INDEX_FILENAME = "generated-file-list.txt";
11
- cwd;
12
6
  #logger;
13
7
  #outputDir;
14
8
  constructor(outputDir, logger) {
15
- this.cwd = process.cwd();
16
- this.#outputDir = isAbsolute(outputDir) ? outputDir : resolve(this.cwd, outputDir);
9
+ this.#outputDir = outputDir;
17
10
  this.#logger = logger;
18
11
  logger.global.debug(`Output directory: ${this.#outputDir}`);
19
12
  }
20
13
  /**
21
- * Gets the list of files generated the last time. The info is found in {@link INDEX_FILENAME}.
14
+ * Gets the list of files generated the last time. The info is found in {@link GENERATED_LIST_FILENAME}.
22
15
  * @returns a list of files that have been generated by us
23
16
  */
24
- async getGeneratedFiles() {
17
+ async getExistingGeneratedFiles() {
25
18
  const files = new Set();
26
19
  try {
27
- const indexFileContents = await this.read(this.resolveGeneratedFile(this.constructor.INDEX_FILENAME));
28
- indexFileContents.split("\n").filter((n) => n.length).forEach((fileName) => files.add(fileName));
20
+ const contents = await this.read(GENERATED_LIST_FILENAME);
21
+ contents.split("\n").filter((n) => n.length).forEach((fileName) => files.add(fileName));
29
22
  } catch (e) {
30
23
  if (!(e instanceof Error && "code" in e && e.code === "ENOENT")) {
31
24
  throw e;
32
25
  }
33
26
  }
34
- return files;
27
+ return Array.from(files);
35
28
  }
36
29
  /**
37
30
  * Cleans the output directory by keeping the generated files and deleting the rest of the given files.
38
31
  *
39
- * @returns a set containing deleted filenames
32
+ * @returns a list with names of deleted files
40
33
  */
41
34
  async cleanOutputDir(generatedFiles, filesToDelete) {
42
35
  this.#logger.global.debug(`Cleaning ${this.#outputDir}`);
43
36
  await mkdir(this.#outputDir, { recursive: true });
44
- generatedFiles.forEach((filename) => {
45
- this.#logger.global.debug(`File ${filename} was re-written, should not delete it`);
46
- filesToDelete.delete(filename);
47
- });
48
- const deletedFiles = new Set(await Promise.all([...filesToDelete].map(async (filename) => {
49
- const resolved = this.resolveGeneratedFile(filename);
50
- if (await GeneratorIO.exists(resolved)) {
51
- this.#logger.global.debug(`Deleting file ${filename}.`);
52
- await rm(resolved);
37
+ const filtered = filesToDelete.filter((item) => !generatedFiles.includes(item));
38
+ return Array.from(new Set(await Promise.all(filtered.map(async (filename) => {
39
+ const url = new URL(filename, this.#outputDir);
40
+ try {
41
+ await rm(url);
42
+ this.#logger.global.debug(`Deleted file ${url}.`);
43
+ return filename;
44
+ } catch (e) {
45
+ this.#logger.global.debug(`Cannot delete file ${url}: ${e instanceof Error ? e.message : String(e)}`);
46
+ return undefined;
53
47
  }
54
- return filename;
55
- })));
56
- return deletedFiles;
57
- }
58
- async createFileIndex(filenames) {
59
- await this.write(this.constructor.INDEX_FILENAME, filenames.join("\n"));
48
+ })).then((files) => files.filter((filename) => filename != null))));
60
49
  }
61
50
  async writeGeneratedFiles(files) {
62
- await this.createFileIndex(files.map((file) => file.name));
63
- this.#logger.global.debug(`created index`);
51
+ await this.write(new File(files.map((file) => `${file.name}\n`), GENERATED_LIST_FILENAME));
64
52
  return Promise.all(files.map(async (file) => {
65
53
  const newFileContent = await file.text();
66
54
  let oldFileContent;
67
55
  try {
68
- oldFileContent = await this.read(this.resolveGeneratedFile(file.name));
56
+ oldFileContent = await this.read(file.name);
69
57
  } catch (_e) {}
70
58
  if (newFileContent !== oldFileContent) {
71
- this.#logger.global.debug(`writing file ${file.name}`);
72
- await this.write(file.name, await file.text());
59
+ await this.write(file);
73
60
  } else {
74
- this.#logger.global.debug(`File ${file.name} stayed the same`);
61
+ this.#logger.global.debug(`File ${new URL(file.name, this.#outputDir)} stayed the same`);
75
62
  }
76
63
  return file.name;
77
64
  }));
78
65
  }
79
- /**
80
- * Checks that a file exists (is visible)
81
- * @param path - the file path to check
82
- */
83
- static async exists(path) {
84
- try {
85
- await access(path, constants.F_OK);
86
- return true;
87
- } catch {
88
- return false;
89
- }
90
- }
91
66
  async loadPlugin(modulePath) {
92
67
  this.#logger.global.debug(`Loading plugin: ${modulePath}`);
93
- const module = await import(pathToFileURL(require.resolve(modulePath)).toString());
68
+ const module = await import(modulePath);
94
69
  const ctr = module.default;
95
70
  if (!Object.prototype.isPrototypeOf.call(Plugin, ctr)) {
96
71
  throw new GeneratorIOException(`Plugin '${modulePath}' is not an instance of a Plugin class`);
97
72
  }
98
73
  return ctr;
99
74
  }
100
- resolveGeneratedFile(filename) {
101
- return resolve(this.#outputDir, filename);
102
- }
103
- async read(path) {
104
- this.#logger.global.debug(`Reading file: ${path}`);
105
- return readFile(path, "utf8");
75
+ async read(filename) {
76
+ const url = new URL(filename, this.#outputDir);
77
+ this.#logger.global.debug(`Reading file: ${url}`);
78
+ return await readFile(url, "utf8");
106
79
  }
107
- async write(filename, content) {
108
- const filePath = join(this.#outputDir, filename);
80
+ async write(file) {
81
+ const filePath = new URL(file.name, this.#outputDir);
109
82
  this.#logger.global.debug(`Writing file ${filePath}.`);
110
- const dir = dirname(filePath);
83
+ const dir = new URL("./", filePath);
111
84
  await mkdir(dir, { recursive: true });
112
- return writeFile(filePath, content, "utf-8");
85
+ return await writeFile(filePath, await file.text(), "utf-8");
113
86
  }
114
87
  }
115
88
  //# sourceMappingURL=./GeneratorIO.js.map
@@ -1 +1 @@
1
- {"mappings":"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","names":["#logger","#outputDir","outputDir: string","logger: LoggerFactory","generatedFiles: string[]","filesToDelete: Set<string>","filenames: string[]","files: readonly File[]","path: string","modulePath: string","module: PluginConstructorModule","ctr: PluginConstructor","filename: string","content: string"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/generator-cli/src/GeneratorIO.ts"],"sourcesContent":["import { constants } from 'node:fs';\nimport { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport Plugin, { type PluginConstructor } from '@vaadin/hilla-generator-core/Plugin.js';\nimport type LoggerFactory from '@vaadin/hilla-generator-utils/LoggerFactory.js';\nimport GeneratorIOException from './GeneratorIOException.js';\n\nconst require = createRequire(import.meta.url);\n\ntype PluginConstructorModule = Readonly<{\n default: PluginConstructor;\n}>;\n\nexport default class GeneratorIO {\n static readonly INDEX_FILENAME = 'generated-file-list.txt';\n declare ['constructor']: typeof GeneratorIO;\n readonly cwd: string;\n readonly #logger: LoggerFactory;\n readonly #outputDir: string;\n\n constructor(outputDir: string, logger: LoggerFactory) {\n this.cwd = process.cwd();\n this.#outputDir = isAbsolute(outputDir) ? outputDir : resolve(this.cwd, outputDir);\n this.#logger = logger;\n\n logger.global.debug(`Output directory: ${this.#outputDir}`);\n }\n\n /**\n * Gets the list of files generated the last time. The info is found in {@link INDEX_FILENAME}.\n * @returns a list of files that have been generated by us\n */\n async getGeneratedFiles(): Promise<Set<string>> {\n const files = new Set<string>();\n try {\n const indexFileContents = await this.read(this.resolveGeneratedFile(this.constructor.INDEX_FILENAME));\n indexFileContents\n .split('\\n')\n .filter((n) => n.length)\n .forEach((fileName) => files.add(fileName));\n } catch (e) {\n // non-existing file is OK, all other errors must be rethrown\n if (!(e instanceof Error && 'code' in e && e.code === 'ENOENT')) {\n throw e;\n }\n }\n return files;\n }\n\n /**\n * Cleans the output directory by keeping the generated files and deleting the rest of the given files.\n *\n * @returns a set containing deleted filenames\n */\n async cleanOutputDir(generatedFiles: string[], filesToDelete: Set<string>): Promise<Set<string>> {\n this.#logger.global.debug(`Cleaning ${this.#outputDir}`);\n await mkdir(this.#outputDir, { recursive: true });\n\n generatedFiles.forEach((filename) => {\n this.#logger.global.debug(`File ${filename} was re-written, should not delete it`);\n filesToDelete.delete(filename);\n });\n\n const deletedFiles = new Set(\n await Promise.all(\n [...filesToDelete].map(async (filename) => {\n const resolved = this.resolveGeneratedFile(filename);\n if (await GeneratorIO.exists(resolved)) {\n this.#logger.global.debug(`Deleting file ${filename}.`);\n await rm(resolved);\n }\n return filename;\n }),\n ),\n );\n\n return deletedFiles;\n }\n\n async createFileIndex(filenames: string[]): Promise<void> {\n await this.write(this.constructor.INDEX_FILENAME, filenames.join('\\n'));\n }\n\n async writeGeneratedFiles(files: readonly File[]): Promise<string[]> {\n await this.createFileIndex(files.map((file) => file.name));\n this.#logger.global.debug(`created index`);\n\n return Promise.all(\n files.map(async (file) => {\n const newFileContent = await file.text();\n let oldFileContent;\n try {\n oldFileContent = await this.read(this.resolveGeneratedFile(file.name));\n } catch (_e) {}\n\n if (newFileContent !== oldFileContent) {\n this.#logger.global.debug(`writing file ${file.name}`);\n await this.write(file.name, await file.text());\n } else {\n this.#logger.global.debug(`File ${file.name} stayed the same`);\n }\n return file.name;\n }),\n );\n }\n\n /**\n * Checks that a file exists (is visible)\n * @param path - the file path to check\n */\n // eslint-disable-next-line class-methods-use-this\n static async exists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n }\n\n async loadPlugin(modulePath: string): Promise<PluginConstructor> {\n this.#logger.global.debug(`Loading plugin: ${modulePath}`);\n const module: PluginConstructorModule = await import(pathToFileURL(require.resolve(modulePath)).toString());\n const ctr: PluginConstructor = module.default;\n\n if (!Object.prototype.isPrototypeOf.call(Plugin, ctr)) {\n throw new GeneratorIOException(`Plugin '${modulePath}' is not an instance of a Plugin class`);\n }\n\n return ctr;\n }\n\n resolveGeneratedFile(filename: string): string {\n return resolve(this.#outputDir, filename);\n }\n\n async read(path: string): Promise<string> {\n this.#logger.global.debug(`Reading file: ${path}`);\n return readFile(path, 'utf8');\n }\n\n async write(filename: string, content: string): Promise<void> {\n const filePath = join(this.#outputDir, filename);\n this.#logger.global.debug(`Writing file ${filePath}.`);\n const dir = dirname(filePath);\n await mkdir(dir, { recursive: true });\n return writeFile(filePath, content, 'utf-8');\n }\n}\n"],"version":3}
1
+ {"mappings":"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","names":["#logger","#outputDir","outputDir: URL","logger: LoggerFactory","generatedFiles: readonly string[]","filesToDelete: readonly string[]","e: unknown","files: readonly File[]","modulePath: string","module: PluginConstructorModule","ctr: PluginConstructor","filename: string","file: File"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/generator-cli/src/GeneratorIO.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/restrict-template-expressions */\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport Plugin, { type PluginConstructor } from '@vaadin/hilla-generator-core/Plugin.js';\nimport type LoggerFactory from '@vaadin/hilla-generator-utils/LoggerFactory.js';\nimport GeneratorIOException from './GeneratorIOException.js';\n\ntype PluginConstructorModule = Readonly<{\n default: PluginConstructor;\n}>;\n\nexport const GENERATED_LIST_FILENAME = 'generated-file-list.txt';\n\nexport default class GeneratorIO {\n readonly #logger: LoggerFactory;\n readonly #outputDir: URL;\n\n constructor(outputDir: URL, logger: LoggerFactory) {\n this.#outputDir = outputDir;\n this.#logger = logger;\n\n logger.global.debug(`Output directory: ${this.#outputDir}`);\n }\n\n /**\n * Gets the list of files generated the last time. The info is found in {@link GENERATED_LIST_FILENAME}.\n * @returns a list of files that have been generated by us\n */\n async getExistingGeneratedFiles(): Promise<readonly string[]> {\n const files = new Set<string>();\n try {\n const contents = await this.read(GENERATED_LIST_FILENAME);\n contents\n .split('\\n')\n .filter((n) => n.length)\n .forEach((fileName) => files.add(fileName));\n } catch (e) {\n // non-existing file is OK, all other errors must be rethrown\n if (!(e instanceof Error && 'code' in e && e.code === 'ENOENT')) {\n throw e;\n }\n }\n return Array.from(files);\n }\n\n /**\n * Cleans the output directory by keeping the generated files and deleting the rest of the given files.\n *\n * @returns a list with names of deleted files\n */\n async cleanOutputDir(\n generatedFiles: readonly string[],\n filesToDelete: readonly string[],\n ): Promise<readonly string[]> {\n this.#logger.global.debug(`Cleaning ${this.#outputDir}`);\n await mkdir(this.#outputDir, { recursive: true });\n\n const filtered = filesToDelete.filter((item) => !generatedFiles.includes(item));\n\n return Array.from(\n new Set(\n await Promise.all(\n filtered.map(async (filename) => {\n const url = new URL(filename, this.#outputDir);\n try {\n await rm(url);\n this.#logger.global.debug(`Deleted file ${url}.`);\n return filename;\n } catch (e: unknown) {\n this.#logger.global.debug(`Cannot delete file ${url}: ${e instanceof Error ? e.message : String(e)}`);\n return undefined;\n }\n }),\n ).then((files) => files.filter((filename) => filename != null)),\n ),\n );\n }\n\n async writeGeneratedFiles(files: readonly File[]): Promise<readonly string[]> {\n await this.write(\n new File(\n files.map((file) => `${file.name}\\n`),\n GENERATED_LIST_FILENAME,\n ),\n );\n\n return Promise.all(\n files.map(async (file) => {\n const newFileContent = await file.text();\n let oldFileContent;\n try {\n oldFileContent = await this.read(file.name);\n } catch (_e) {}\n\n if (newFileContent !== oldFileContent) {\n await this.write(file);\n } else {\n this.#logger.global.debug(`File ${new URL(file.name, this.#outputDir)} stayed the same`);\n }\n return file.name;\n }),\n );\n }\n\n async loadPlugin(modulePath: string): Promise<PluginConstructor> {\n this.#logger.global.debug(`Loading plugin: ${modulePath}`);\n const module: PluginConstructorModule = await import(modulePath);\n const ctr: PluginConstructor = module.default;\n\n if (!Object.prototype.isPrototypeOf.call(Plugin, ctr)) {\n throw new GeneratorIOException(`Plugin '${modulePath}' is not an instance of a Plugin class`);\n }\n\n return ctr;\n }\n\n async read(filename: string): Promise<string> {\n const url = new URL(filename, this.#outputDir);\n this.#logger.global.debug(`Reading file: ${url}`);\n return await readFile(url, 'utf8');\n }\n\n async write(file: File): Promise<void> {\n const filePath = new URL(file.name, this.#outputDir);\n this.#logger.global.debug(`Writing file ${filePath}.`);\n const dir = new URL('./', filePath);\n await mkdir(dir, { recursive: true });\n return await writeFile(filePath, await file.text(), 'utf-8');\n }\n}\n"],"version":3}
package/index.js CHANGED
@@ -1,49 +1,68 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { isAbsolute, join, sep } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { parseArgs } from "node:util";
1
5
  import Generator from "@vaadin/hilla-generator-core/Generator.js";
2
6
  import LoggerFactory from "@vaadin/hilla-generator-utils/LoggerFactory.js";
3
- import meow from "meow";
4
7
  import GeneratorIO from "./GeneratorIO.js";
5
- import { processInput } from "./utils.js";
6
- const { flags: { outputDir, plugin: plugins, verbose }, input: [input] } = meow(`
7
- Usage:
8
- tsgen
9
- (will read JSON from stdin)
10
- tsgen <OpenAPI JSON string>
11
- tsgen <OpenAPI file path>
12
-
13
- Options:
14
- -h, --help Show this screen
15
- -o, --output-dir Output directory
16
- -p, --plugin <path> Use the plugin loadable by <path>.
17
- --version Show the app version
18
- `, {
19
- flags: {
8
+ const { positionals: [file], values: { help, outputDir, plugin: plugins, verbose, version } } = parseArgs({
9
+ options: {
10
+ inputFile: {
11
+ type: "string",
12
+ short: "i"
13
+ },
14
+ help: { type: "boolean" },
20
15
  outputDir: {
21
16
  default: "frontend/generated",
22
- shortFlag: "o",
17
+ short: "o",
23
18
  type: "string"
24
19
  },
25
20
  plugin: {
26
21
  default: [],
27
- isMultiple: true,
28
- shortFlag: "p",
22
+ multiple: true,
23
+ short: "p",
29
24
  type: "string"
30
25
  },
31
26
  verbose: {
32
- shortFlag: "v",
27
+ short: "v",
33
28
  type: "boolean"
34
- }
29
+ },
30
+ version: { type: "boolean" }
35
31
  },
36
- importMeta: import.meta
32
+ allowPositionals: true
37
33
  });
38
- const logger = new LoggerFactory({ verbose });
39
- const io = new GeneratorIO(outputDir, logger);
40
- const resolvedPlugins = await Promise.all(Array.from(new Set(plugins), async (pluginPath) => io.loadPlugin(pluginPath)));
41
- const generator = new Generator(resolvedPlugins, {
42
- logger,
43
- outputDir
44
- });
45
- const files = await generator.process(await processInput(io, input));
46
- const filesToDelete = await io.getGeneratedFiles();
47
- const generatedFiles = await io.writeGeneratedFiles(files);
48
- await io.cleanOutputDir(generatedFiles, filesToDelete);
34
+ if (help) {
35
+ console.log(`Usage:
36
+ tsgen <file> [OPTIONS]
37
+
38
+ Arguments:
39
+ file OpenAPI JSON schema file (required)
40
+
41
+ Options:
42
+ -h, --help Show this screen
43
+ -o, --output-dir Output directory
44
+ -p, --plugin <path> Use the plugin loadable by <path>.
45
+ -v, --verbose Enable verbose logging
46
+ --version Show the app version
47
+ `);
48
+ } else if (version) {
49
+ const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
50
+ console.log(packageJson.version);
51
+ } else {
52
+ if (!file) {
53
+ throw new Error("OpenAPI file is required");
54
+ }
55
+ const logger = new LoggerFactory({ verbose });
56
+ const outputDirPath = isAbsolute(outputDir) ? outputDir : join(process.cwd(), outputDir);
57
+ const io = new GeneratorIO(pathToFileURL(outputDirPath.endsWith(sep) ? outputDirPath : `${outputDirPath}${sep}`), logger);
58
+ const resolvedPlugins = await Promise.all(Array.from(new Set(plugins), async (pluginPath) => io.loadPlugin(pluginPath)));
59
+ const generator = new Generator(resolvedPlugins, {
60
+ logger,
61
+ outputDir
62
+ });
63
+ const files = await generator.process(await readFile(file, "utf8"));
64
+ const filesToDelete = await io.getExistingGeneratedFiles();
65
+ const generatedFiles = await io.writeGeneratedFiles(files);
66
+ await io.cleanOutputDir(generatedFiles, filesToDelete);
67
+ }
49
68
  //# sourceMappingURL=./index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":"AAAA,OAAO,0DAA2D;AAClE,OAAO,mEAAoE;AAC3E,OAAO,gBAAiB;AACxB,OAAO,mCAAoC;AAC3C,SAAS,gCAAiC;AAE1C,MAAM,EACJ,OAAO,EAAE,WAAW,QAAQ,SAAS,SAAS,EAC9C,OAAO,CAAC,MAAM,EACf,GAAG,MACD;;;;;;;;;;;;GAaD;CACE,OAAO;EACL,WAAW;GACT,SAAS;GACT,WAAW;GACX,MAAM;EACP;EACD,QAAQ;GACN,SAAS,CAAE;GACX,YAAY;GACZ,WAAW;GACX,MAAM;EACP;EACD,SAAS;GACP,WAAW;GACX,MAAM;EACP;CACF;CACD,YAAY,OAAO;AACpB,EACF;AAED,MAAM,SAAS,IAAI,cAAc,EAAE,QAAS;AAE5C,MAAM,KAAK,IAAI,YAAY,WAAW;AAEtC,MAAM,kBAAkB,MAAM,QAAQ,IACpC,MAAM,KAAK,IAAI,IAAI,UAAU,OAAO,eAAe,GAAG,WAAW,WAAW,CAAC,CAC9E;AACD,MAAM,YAAY,IAAI,UAAU,iBAAiB;CAAE;CAAQ;AAAW;AAEtE,MAAM,QAAQ,MAAM,UAAU,QAAQ,MAAM,aAAa,IAAI,MAAM,CAAC;AACpE,MAAM,gBAAgB,MAAM,GAAG,mBAAmB;AAClD,MAAM,iBAAiB,MAAM,GAAG,oBAAoB,MAAM;AAE1D,MAAM,GAAG,eAAe,gBAAgB,cAAc","names":[],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/generator-cli/src/index.ts"],"sourcesContent":["import Generator from '@vaadin/hilla-generator-core/Generator.js';\nimport LoggerFactory from '@vaadin/hilla-generator-utils/LoggerFactory.js';\nimport meow from 'meow';\nimport GeneratorIO from './GeneratorIO.js';\nimport { processInput } from './utils.js';\n\nconst {\n flags: { outputDir, plugin: plugins, verbose },\n input: [input],\n} = meow(\n `\nUsage:\n tsgen\n (will read JSON from stdin)\n tsgen <OpenAPI JSON string>\n tsgen <OpenAPI file path>\n\nOptions:\n -h, --help Show this screen\n -o, --output-dir Output directory\n -p, --plugin <path> Use the plugin loadable by <path>.\n --version Show the app version\n`,\n {\n flags: {\n outputDir: {\n default: 'frontend/generated',\n shortFlag: 'o',\n type: 'string',\n },\n plugin: {\n default: [],\n isMultiple: true,\n shortFlag: 'p',\n type: 'string',\n },\n verbose: {\n shortFlag: 'v',\n type: 'boolean',\n },\n },\n importMeta: import.meta,\n },\n);\n\nconst logger = new LoggerFactory({ verbose });\n\nconst io = new GeneratorIO(outputDir, logger);\n\nconst resolvedPlugins = await Promise.all(\n Array.from(new Set(plugins), async (pluginPath) => io.loadPlugin(pluginPath)),\n);\nconst generator = new Generator(resolvedPlugins, { logger, outputDir });\n\nconst files = await generator.process(await processInput(io, input));\nconst filesToDelete = await io.getGeneratedFiles();\nconst generatedFiles = await io.writeGeneratedFiles(files);\n\nawait io.cleanOutputDir(generatedFiles, filesToDelete);\n"],"version":3}
1
+ {"mappings":"AAAA,SAAS,kCAAmC;AAC5C,SAAS,YAAY,MAAM,sBAAuB;AAClD,SAAS,+BAAgC;AACzC,SAAS,4BAA6B;AACtC,OAAO,0DAA2D;AAClE,OAAO,mEAAoE;AAE3E,OAAO,mCAAoC;AAE3C,MAAM,EACJ,aAAa,CAAC,KAAK,EACnB,QAAQ,EAAE,MAAM,WAAW,QAAQ,SAAS,SAAS,SAAS,EAC/D,GAAG,UAAU;CACZ,SAAS;EACP,WAAW;GACT,MAAM;GACN,OAAO;EACR;EACD,MAAM,EACJ,MAAM,UACP;EACD,WAAW;GACT,SAAS;GACT,OAAO;GACP,MAAM;EACP;EACD,QAAQ;GACN,SAAS,CAAE;GACX,UAAU;GACV,OAAO;GACP,MAAM;EACP;EACD,SAAS;GACP,OAAO;GACP,MAAM;EACP;EACD,SAAS,EACP,MAAM,UACP;CACF;CACD,kBAAkB;AACnB,EAAC;AAEF,IAAI,MAAM;AAER,SAAQ,KAAK;;;;;;;;;;;;EAYb;AACD,WAAU,SAAS;CAClB,MAAMA,cAA2B,KAAK,MAAM,MAAM,SAAS,IAAI,IAAI,mBAAmB,OAAO,KAAK,MAAM,OAAO,CAAC;AAEhH,SAAQ,IAAI,YAAY,QAAQ;AACjC,OAAM;AACL,MAAK,MAAM;AACT,QAAM,IAAI,MAAM;CACjB;CAED,MAAM,SAAS,IAAI,cAAc,EAAE,QAAS;CAE5C,MAAM,gBAAgB,WAAW,UAAU,GAAG,YAAY,KAAK,QAAQ,KAAK,EAAE,UAAU;CAExF,MAAM,KAAK,IAAI,YACb,cAAc,cAAc,SAAS,IAAI,GAAG,iBAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,EACrF;CAGF,MAAM,kBAAkB,MAAM,QAAQ,IACpC,MAAM,KAAK,IAAI,IAAI,UAAU,OAAO,eAAe,GAAG,WAAW,WAAW,CAAC,CAC9E;CACD,MAAM,YAAY,IAAI,UAAU,iBAAiB;EAAE;EAAQ;CAAW;CAEtE,MAAM,QAAQ,MAAM,UAAU,QAAQ,MAAM,SAAS,MAAM,OAAO,CAAC;CACnE,MAAM,gBAAgB,MAAM,GAAG,2BAA2B;CAC1D,MAAM,iBAAiB,MAAM,GAAG,oBAAoB,MAAM;AAE1D,OAAM,GAAG,eAAe,gBAAgB,cAAc;AACvD","names":["packageJson: PackageJson"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/generator-cli/src/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { isAbsolute, join, sep } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport Generator from '@vaadin/hilla-generator-core/Generator.js';\nimport LoggerFactory from '@vaadin/hilla-generator-utils/LoggerFactory.js';\nimport type { PackageJson } from 'type-fest';\nimport GeneratorIO from './GeneratorIO.js';\n\nconst {\n positionals: [file],\n values: { help, outputDir, plugin: plugins, verbose, version },\n} = parseArgs({\n options: {\n inputFile: {\n type: 'string',\n short: 'i',\n },\n help: {\n type: 'boolean',\n },\n outputDir: {\n default: 'frontend/generated',\n short: 'o',\n type: 'string',\n },\n plugin: {\n default: [],\n multiple: true,\n short: 'p',\n type: 'string',\n },\n verbose: {\n short: 'v',\n type: 'boolean',\n },\n version: {\n type: 'boolean',\n },\n },\n allowPositionals: true,\n});\n\nif (help) {\n // eslint-disable-next-line no-console\n console.log(`Usage:\ntsgen <file> [OPTIONS]\n\nArguments:\n file OpenAPI JSON schema file (required)\n\nOptions:\n -h, --help Show this screen\n -o, --output-dir Output directory\n -p, --plugin <path> Use the plugin loadable by <path>.\n -v, --verbose Enable verbose logging\n --version Show the app version\n`);\n} else if (version) {\n const packageJson: PackageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));\n // eslint-disable-next-line no-console\n console.log(packageJson.version);\n} else {\n if (!file) {\n throw new Error('OpenAPI file is required');\n }\n\n const logger = new LoggerFactory({ verbose });\n\n const outputDirPath = isAbsolute(outputDir) ? outputDir : join(process.cwd(), outputDir);\n\n const io = new GeneratorIO(\n pathToFileURL(outputDirPath.endsWith(sep) ? outputDirPath : `${outputDirPath}${sep}`),\n logger,\n );\n\n const resolvedPlugins = await Promise.all(\n Array.from(new Set(plugins), async (pluginPath) => io.loadPlugin(pluginPath)),\n );\n const generator = new Generator(resolvedPlugins, { logger, outputDir });\n\n const files = await generator.process(await readFile(file, 'utf8'));\n const filesToDelete = await io.getExistingGeneratedFiles();\n const generatedFiles = await io.writeGeneratedFiles(files);\n\n await io.cleanOutputDir(generatedFiles, filesToDelete);\n}\n"],"version":3}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-generator-cli",
3
- "version": "24.8.0-alpha1",
3
+ "version": "24.8.0-alpha2",
4
4
  "description": "A Hilla tool to generate TypeScript code from the OpenAPI document",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -43,9 +43,7 @@
43
43
  "tsgen": "bin/index.js"
44
44
  },
45
45
  "dependencies": {
46
- "@vaadin/hilla-generator-core": "24.8.0-alpha1",
47
- "@vaadin/hilla-generator-utils": "24.8.0-alpha1",
48
- "get-stdin": "9.0.0",
49
- "meow": "13.2.0"
46
+ "@vaadin/hilla-generator-core": "24.8.0-alpha2",
47
+ "@vaadin/hilla-generator-utils": "24.8.0-alpha2"
50
48
  }
51
49
  }
package/utils.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type GeneratorIO from "./GeneratorIO.js";
2
- export declare function processInput(io: GeneratorIO, raw?: string): Promise<string>;
package/utils.js DELETED
@@ -1,16 +0,0 @@
1
- import { isAbsolute, resolve } from "path";
2
- import getStdin from "get-stdin";
3
- export async function processInput(io, raw) {
4
- if (raw) {
5
- let result = raw;
6
- if (result.startsWith("'") || result.startsWith("\"")) {
7
- result = raw.substring(1, raw.length - 1);
8
- }
9
- if (result.startsWith("{")) {
10
- return result;
11
- }
12
- return io.read(isAbsolute(result) ? result : resolve(io.cwd, result));
13
- }
14
- return getStdin();
15
- }
16
- //# sourceMappingURL=./utils.js.map
package/utils.js.map DELETED
@@ -1 +0,0 @@
1
- {"mappings":"AAAA,SAAS,YAAY,qBAAsB;AAC3C,OAAO,yBAA0B;AAGjC,OAAO,eAAe,aAAaA,IAAiBC,KAA+B;AACjF,KAAI,KAAK;EACP,IAAI,SAAS;AAEb,MAAI,OAAO,WAAW,IAAI,IAAI,OAAO,WAAW,KAAI,EAAE;AACpD,YAAS,IAAI,UAAU,GAAG,IAAI,SAAS,EAAE;EAC1C;AAED,MAAI,OAAO,WAAW,IAAI,EAAE;AAC1B,UAAO;EACR;AAED,SAAO,GAAG,KAAK,WAAW,OAAO,GAAG,SAAS,QAAQ,GAAG,KAAK,OAAO,CAAC;CACtE;AAED,QAAO,UAAU;AAClB","names":["io: GeneratorIO","raw?: string"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/generator-cli/src/utils.ts"],"sourcesContent":["import { isAbsolute, resolve } from 'path';\nimport getStdin from 'get-stdin';\nimport type GeneratorIO from './GeneratorIO.js';\n\nexport async function processInput(io: GeneratorIO, raw?: string): Promise<string> {\n if (raw) {\n let result = raw;\n\n if (result.startsWith(\"'\") || result.startsWith('\"')) {\n result = raw.substring(1, raw.length - 1);\n }\n\n if (result.startsWith('{')) {\n return result;\n }\n\n return io.read(isAbsolute(result) ? result : resolve(io.cwd, result));\n }\n\n return getStdin();\n}\n"],"version":3}