Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

idea: use json-schema-to-ts in ajv plugin for ide schema suggestions #480

Open
1 task done
dec0dOS opened this issue Sep 20, 2023 · 3 comments
Open
1 task done

Comments

@dec0dOS
Copy link

dec0dOS commented Sep 20, 2023

Issue type

  • idea

First of all, I'd like to express my gratitude for sharing your work with the open-source community. The Qiwi Node.js packages and their maintainer, @antongolub, are truly hidden gems within the npm ecosystem. They offer packages with minimal dependencies and boast high-quality code.

I've been on the hunt for a reliable Node.js configuration library for quite some time now, but most of them don't quite meet my requirements.

It seems that Uniconfig might be a perfect fit for my needs. The only thing that's been on my mind is the potential to achieve type completion for the configuration file based on an ajv schema. I've come across the possibility of doing this with the help of https://github.com/ThomasAribart/json-schema-to-ts if the schema is defined within the code directly, rather than in the configuration file.

So, I was wondering if it's feasible to implement this feature in Uniconfig.

@antongolub
Copy link
Member

antongolub commented Sep 21, 2023

Hey, @dec0dOS,

Thanks for the feedback. We're also thinking about this problem: some our configs turn out to be large and nested, it brings complexity and types strengthening might be reasonable. Once (not in this repo) we've tried to implement json schema cast to TS types through intermediate codegen / ttypescript hooks. Ironic, but we did the exact opposite: annotated DTOs with metadata were transformed into (very limited) schemas in runtime. I think we should try again)

And thanks for the tip, we'll definitely look into the project.

@dec0dOS
Copy link
Author

dec0dOS commented Sep 21, 2023

Right now, it seems that accomplishing this task is quite straightforward with the help of https://quicktype.io/. However, for my specific needs, it's essential to completely avoid the build step if possible. That's why I'm using JSDoc with TypeScript, along with ESLint as a linter. It works incredibly well, by the way. Therefore, having a tool like json-schema-to-ts seems like a more interesting solution.

@dec0dOS
Copy link
Author

dec0dOS commented Sep 22, 2023

It appears that there's no need for any integration with the library if you're planning to pass only the data property from the Config

Here's an example:

import fs from "node:fs";
import path from "node:path";

import { asConst } from "json-schema-to-ts";

import { Config, rollupPlugin } from "@qiwi/uniconfig-core";
import uniconfigPluginApiFile from "@qiwi/uniconfig-plugin-api-file";
import uniconfigPluginAjv from "@qiwi/uniconfig-plugin-ajv";
import uniconfigPluginEnv from "@qiwi/uniconfig-plugin-env";
import uniconfigPluginYaml from "@qiwi/uniconfig-plugin-yaml";

import * as constants from "./constants.js";

rollupPlugin(uniconfigPluginApiFile.pipe);
rollupPlugin(uniconfigPluginAjv.pipe);
rollupPlugin(uniconfigPluginEnv.pipe);
rollupPlugin(uniconfigPluginYaml.pipe);

// example schema from https://github.com/ThomasAribart/json-schema-to-ts
const configSchema = asConst({
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "integer" },
    hobbies: { type: "array", items: { type: "string" } },
    favoriteFood: { enum: ["pizza", "taco", "fries"] },
  },
  required: ["name", "age"],
});

/**
 * @typedef {import("json-schema-to-ts").FromSchema<configSchema>} ConfigData
 */

/**
 * @returns {ConfigData} Config data
 */
export function loadConfig() {
  let configPath = path.join(constants.CONFIG_DIRECTORY, constants.CONFIG_FILENAME);
  if (!fs.existsSync(configPath)) {
    throw new Error(`Couldn't find config file at ${configPath}`);
  }

  const checkConfig = (configData, schema) => {
    new Config({
      data: { data: configData, schema: schema },
      mode: "sync",
      pipeline: "ajv",
    });
  };

  const config = new Config({
    data: configPath,
    mode: "sync",
    pipeline: "file>yaml",
  });

  const configData = config.data;
  checkConfig(configData, configSchema);

  return configData;
}

I'm getting the expected code completion:
Screenshot 2023-09-22 at 01 30 24

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants