Skip to content

Commit

Permalink
From XO to biome
Browse files Browse the repository at this point in the history
  • Loading branch information
OzzyCzech committed Dec 9, 2024
1 parent 1120d80 commit f1a8a4f
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 2,302 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ jobs:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: yarn install --ignore-engines
- name: Code quality
run: yarn biome ci .
- name: Test
run: yarn test
17 changes: 17 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"files": {
"ignore": ["examples/**/*.*"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"lineWidth": 240
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true
}
}
36 changes: 20 additions & 16 deletions cli.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env node
import process from 'node:process';
import {parse} from 'node:path';
import meow from 'meow';
import chalk from 'chalk';
import {globby} from 'globby';
import {toOpenApi, readJson, writeYaml} from './src/index.js';

const cli = meow(`
import parse from "node:path";
import { process } from "node:process";
import chalk from "chalk";
import { globby } from "globby";
import meow from "meow";
import { readJson, toOpenApi, writeYaml } from "./src/index.js";

const cli = meow(
`
Usage
$ mock-to-openapi <input> <output>
Expand All @@ -17,26 +18,29 @@ const cli = meow(`
Examples
$ mock-to-openapi ~/Downloads/*.json ~/Downloads/API
`, {
importMeta: import.meta, flags: {
verbose: {type: 'boolean', shortFlag: 'v'},
write: {type: 'boolean', default: true, shortFlag: 'w'},
`,
{
importMeta: import.meta,
flags: {
verbose: { type: "boolean", shortFlag: "v" },
write: { type: "boolean", default: true, shortFlag: "w" },
},
},
});
);

// Check if input was set
if (cli.input.length === 0) {
cli.showHelp(2);
}

const verbose = message => {
const verbose = (message) => {
if (cli.flags.verbose) {
console.log(chalk.gray(message));
}
};

// Search for json files
const files = await globby(cli.input[0], {expandDirectories: {extensions: ['json']}});
const files = await globby(cli.input[0], { expandDirectories: { extensions: ["json"] } });

// Process JSON files
if (files.length > 0) {
Expand All @@ -57,6 +61,6 @@ if (files.length > 0) {

console.log(chalk.green(`Done! ${files.length} JSON file(s) successfully processed`));
} else {
console.error(chalk.red('ERROR: I didn\'t find any json files'));
console.error(chalk.red("ERROR: I didn't find any json files"));
process.exit(1);
}
74 changes: 32 additions & 42 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,44 +1,34 @@
{
"name": "mock-to-openapi",
"version": "1.1.2",
"description": "Cli tool (and library) for converting JSON mock objects to Open API schemas",
"type": "module",
"exports": "./src/index.js",
"license": "MIT",
"repository": "OzzyCzech/mock-to-openapi",
"author": "Roman Ožana <[email protected]> (https://ozana.cz)",
"bin": {
"mock-to-openapi": "cli.js"
},
"keywords": [
"openapi",
"converter",
"swagger",
"yaml",
"json",
"cli"
],
"scripts": {
"test": "xo && ava",
"release": "np --no-release-draft",
"examples": "./cli.js ./examples"
},
"private": false,
"dependencies": {
"chalk": "^5.3.0",
"globby": "^14.0.2",
"luxon": "^3.5.0",
"meow": "^13.2.0",
"yaml": "^2.6.1"
},
"devDependencies": {
"ava": "^6.2.0",
"np": "^10.1.0",
"xo": "^0.60.0"
},
"xo": {
"rules": {
"no-await-in-loop": 0
}
}
"name": "mock-to-openapi",
"version": "1.1.2",
"description": "Cli tool (and library) for converting JSON mock objects to Open API schemas",
"type": "module",
"exports": "./src/index.js",
"license": "MIT",
"repository": "OzzyCzech/mock-to-openapi",
"author": "Roman Ožana <[email protected]> (https://ozana.cz)",
"bin": {
"mock-to-openapi": "cli.js"
},
"keywords": ["openapi", "converter", "swagger", "yaml", "json", "cli"],
"scripts": {
"test": "ava",
"format": "biome format --write",
"lint": "biome lint",
"release": "np --no-release-draft",
"examples": "./cli.js ./examples"
},
"private": false,
"dependencies": {
"@biomejs/biome": "^1.9.4",
"chalk": "^5.3.0",
"globby": "^14.0.2",
"luxon": "^3.5.0",
"meow": "^13.2.0",
"yaml": "^2.6.1"
},
"devDependencies": {
"ava": "^6.2.0",
"np": "^10.1.0"
}
}
37 changes: 14 additions & 23 deletions src/get-format.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {Buffer} from 'node:buffer';
import {DateTime} from 'luxon';
import {getType} from './get-type.js';
import { Buffer } from "node:buffer";
import { DateTime } from "luxon";
import { getType } from "./get-type.js";

/**
* Detect base64 string
* @returns {boolean}
* @param string
*/
function isBase64(string) {
const dec = Buffer.from(string, 'base64').toString('utf8');
return string === Buffer.from(dec, 'binary').toString('base64');
const dec = Buffer.from(string, "base64").toString("utf8");
return string === Buffer.from(dec, "binary").toString("base64");
}

/**
Expand All @@ -20,37 +20,28 @@ function isBase64(string) {
*/
export function getFormat(item) {
// Integers range detection
if (getType(item) === 'integer') {
if (getType(item) === "integer") {
if (item > -2_147_483_647 && item < 2_147_483_647) {
return 'int32';
return "int32";
}

return Number.isSafeInteger(item) ? 'int64' : 'unsafe';
return Number.isSafeInteger(item) ? "int64" : "unsafe";
}

if (getType(item) === 'string') {
if (getType(item) === "string") {
if (!Number.isNaN(Date.parse(item))) {
if (
DateTime.fromFormat(item, 'yyyy').isValid
|| DateTime.fromFormat(item, 'yyyy-MM').isValid
|| DateTime.fromFormat(item, 'yyyy-MM-dd').isValid
) {
return 'date';
if (DateTime.fromFormat(item, "yyyy").isValid || DateTime.fromFormat(item, "yyyy-MM").isValid || DateTime.fromFormat(item, "yyyy-MM-dd").isValid) {
return "date";
}

if (
DateTime.fromSQL(item).isValid
|| DateTime.fromISO(item).isValid
|| DateTime.fromHTTP(item).isValid
|| DateTime.fromRFC2822(item).isValid
) {
return 'date-time';
if (DateTime.fromSQL(item).isValid || DateTime.fromISO(item).isValid || DateTime.fromHTTP(item).isValid || DateTime.fromRFC2822(item).isValid) {
return "date-time";
}
}

// Base64 encoded data
if (item && isBase64(item)) {
return 'byte';
return "byte";
}
}
}
20 changes: 10 additions & 10 deletions src/get-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@
* @returns {string|string}
*/
export function getType(item) {
if (typeof item === 'string') {
return 'string';
if (typeof item === "string") {
return "string";
}

if (typeof item === 'number') {
return Number.isInteger(item) ? 'integer' : 'number';
if (typeof item === "number") {
return Number.isInteger(item) ? "integer" : "number";
}

if (Object.prototype.toString.call(item) === '[object Array]') {
return 'array';
if (Object.prototype.toString.call(item) === "[object Array]") {
return "array";
}

if (item && typeof item === 'object') {
return 'object';
if (item && typeof item === "object") {
return "object";
}

if (typeof item === 'boolean') {
return 'boolean';
if (typeof item === "boolean") {
return "boolean";
}
}
6 changes: 3 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export {toOpenApi} from './to-open-api.js';
export {readJson} from './read-json.js';
export {writeYaml} from './write-yaml.js';
export { toOpenApi } from "./to-open-api.js";
export { readJson } from "./read-json.js";
export { writeYaml } from "./write-yaml.js";
4 changes: 2 additions & 2 deletions src/read-json.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {readFile as readFileAsync} from 'node:fs/promises';
import { readFile as readFileAsync } from "node:fs/promises";

/**
* Read file content as string
* @param {string} path
* @returns {Promise<*>}
*/
export async function readJson(path) {
return JSON.parse(await readFileAsync(path, 'utf8'));
return JSON.parse(await readFileAsync(path, "utf8"));
}
30 changes: 15 additions & 15 deletions src/to-open-api.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getFormat } from "./get-format.js";
/**
* Convert item to OpenAPI schema
* @param item
* @returns {{format: (string), type: string, example: number}|{type: string, items: {type: ("undefined"|"object"|"boolean"|"number"|"string"|"function"|"symbol"|"bigint")}}|{format: string, type: string}|{}|{type: string, example: string}|{type: string, example: number}|{type: string, example: boolean}|{format: string, type: string, example: number}}
*/
import {getType} from './get-type.js';
import {getFormat} from './get-format.js';
import { getType } from "./get-type.js";

export function toOpenApi(item) {
const oa = {};
Expand All @@ -14,8 +14,8 @@ export function toOpenApi(item) {
const example = item;

switch (type) {
case 'object': {
oa.type = 'object';
case "object": {
oa.type = "object";
oa.properties = {};
for (const [key, value] of Object.entries(item)) {
oa.properties[key] = toOpenApi(value);
Expand All @@ -24,28 +24,28 @@ export function toOpenApi(item) {
break;
}

case 'array': {
return {type, items: toOpenApi(item[0])};
case "array": {
return { type, items: toOpenApi(item[0]) };
}

case 'integer': {
return {type, format, example};
case "integer": {
return { type, format, example };
}

case 'number': {
return {type, example};
case "number": {
return { type, example };
}

case 'boolean': {
return {type, example};
case "boolean": {
return { type, example };
}

case 'string': {
return format ? {type, format, example} : {type, example};
case "string": {
return format ? { type, format, example } : { type, example };
}

default: {
return {type: 'string', format: 'nullable'};
return { type: "string", format: "nullable" };
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/write-yaml.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {mkdir, writeFile as writeFileAsync} from 'node:fs/promises';
import {existsSync} from 'node:fs';
import {dirname} from 'node:path';
import * as YAML from 'yaml';
import { existsSync } from "node:fs";
import { mkdir, writeFile as writeFileAsync } from "node:fs/promises";
import { dirname } from "node:path";
import * as YAML from "yaml";

/**
* Write content to the file and create directory if not exists
Expand All @@ -12,7 +12,7 @@ import * as YAML from 'yaml';
*/
export async function writeYaml(file, content) {
if (!existsSync(dirname(file))) {
await mkdir(dirname(file), {recursive: true});
await mkdir(dirname(file), { recursive: true });
}

return writeFileAsync(file, YAML.stringify(content));
Expand Down
Loading

0 comments on commit f1a8a4f

Please sign in to comment.