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

Add more features to "deno init" subcommand #12781

Closed
tani opened this issue Nov 16, 2021 · 13 comments
Closed

Add more features to "deno init" subcommand #12781

tani opened this issue Nov 16, 2021 · 13 comments
Labels
suggestion suggestions for new features (yet to be agreed) user feedback wanted feedback from the community is desired

Comments

@tani
Copy link

tani commented Nov 16, 2021

Related to #5040, we propose the deno.json config generator like tsc --init for tsconfig.json.

Background

Because of the increasing configuration parameters, deno developers employed the configuration file deno.json to keep the configuration parameter for each project.

Problem

As there exist many parameters, it is hard to know what parameter we can use.
In the close future, deno may have more options to tweak their behaviour.
We need to write a new configuration file from a scratch for each project.

Solution

We already have a JSON Schema to validate a configuration file.
The schema file contains the default value information.
Thus, we can mechanically generate the default configuration file from this schema.

Related work

We can see similar functions in the related project.

  • npm init
  • yarn init
  • cargo init
  • tsc --init
  • eslint --init

We also have the counterexample. babel does not provide a init command.

Proof of Concept

function generate(schema: any): any {
  if ("default" in schema) {
    return schema.default;
  }
  if ("type" in schema) {
    switch (schema.type) {
      case "object":
        return Object.fromEntries(
          Object.entries(schema.properties).map(([key, value]) => {
            return [key, generate(value)];
          }),
        );
      case "array":
        return [];
    }
  }
}

const schemaUrl =
  "https://deno.land/x/[email protected]/cli/schemas/config-file.v1.json";
const response = await fetch(schemaUrl);
const schema = await response.json();
const configFile = "deno.json";
Deno.writeTextFile(configFile, JSON.stringify(generate(schema), null, 2));
{
  "compilerOptions": {
    "allowJs": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "checkJs": false,
    "experimentalDecorators": true,
    "jsx": "react",
    "jsxFactory": "React.createElement",
    "jsxFragmentFactory": "React.Fragment",
    "jsxImportSource": "react",
    "keyofStringsOnly": false,
    "lib": [
      "deno.window"
    ],
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": true,
    "noImplicitReturns": false,
    "noImplicitThis": true,
    "noImplicitUseStrict": true,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noUncheckedIndexedAccess": false,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictNullChecks": true,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false
  },
  "lint": {
    "files": {
      "include": [],
      "exclude": []
    },
    "rules": {
      "tags": [],
      "exclude": [],
      "include": []
    }
  },
  "fmt": {
    "files": {
      "include": [],
      "exclude": []
    },
    "options": {
      "useTabs": false,
      "lineWidth": 80,
      "indentWidth": 2,
      "singleQuote": false,
      "proseWrap": "always"
    }
  }
}
@bartlomieju bartlomieju added suggestion suggestions for new features (yet to be agreed) user feedback wanted feedback from the community is desired labels Nov 30, 2021
@tani
Copy link
Author

tani commented Dec 1, 2021

I have created an installable package. Please try it. Thanks.

https://github.com/tani/deno-init

@dsherret
Copy link
Member

I don't think we want to enoucrage many projects to be created with all the possible compiler options because generally people should not be changing any of the compiler options.

If we do have this command, perhaps just have all the settings mostly empty, which should be a sufficient starting point. The LSP provides autocomplete to explore other options.

@kitsonk
Copy link
Contributor

kitsonk commented Jan 17, 2022

I am also not in favour. The CLI is designed to have sensible defaults and to allow incremental changes when those defaults don't make sense for a particular context. That would reduce an init command to writing out an empty deno.jsonc, which doesn't make a huge amount of sense. We should always aim to keep config minimal and by "exception" and an init feels like it would undermine that principal.

@jsejcksn
Copy link
Contributor

jsejcksn commented Feb 5, 2022

If we do have this command, perhaps just have all the settings mostly empty

Rather than empty, this could be solved by generating the file as .jsonc with all defaults present as literals, and all lines in the object commented out. Doing so would preserve all defaults (even if they change in subsequent releases) while still providing a template for accessible discovery without tooling and quick enablement/customization of individual options.

Example deno.jsonc:
{
  // "compilerOptions": {
  //   "allowJs": true,
  //   "allowUnreachableCode": false,
  //   "allowUnusedLabels": false,
  //   "checkJs": false,
  //   "experimentalDecorators": true,
  //   "jsx": "react",
  //   "jsxFactory": "React.createElement",
  //   "jsxFragmentFactory": "React.Fragment",
  //   "jsxImportSource": "react",
  //   "keyofStringsOnly": false,
  //   "lib": [
  //     "deno.window"
  //   ],
  //   "noFallthroughCasesInSwitch": false,
  //   "noImplicitAny": true,
  //   "noImplicitReturns": false,
  //   "noImplicitThis": true,
  //   "noImplicitUseStrict": true,
  //   "noStrictGenericChecks": false,
  //   "noUnusedLocals": false,
  //   "noUnusedParameters": false,
  //   "noUncheckedIndexedAccess": false,
  //   "strict": true,
  //   "strictBindCallApply": true,
  //   "strictFunctionTypes": true,
  //   "strictPropertyInitialization": true,
  //   "strictNullChecks": true,
  //   "suppressExcessPropertyErrors": false,
  //   "suppressImplicitAnyIndexErrors": false
  // },
  // "lint": {
  //   "files": {
  //     "include": [],
  //     "exclude": []
  //   },
  //   "rules": {
  //     "tags": [],
  //     "exclude": [],
  //     "include": []
  //   }
  // },
  // "fmt": {
  //   "files": {
  //     "include": [],
  //     "exclude": []
  //   },
  //   "options": {
  //     "useTabs": false,
  //     "lineWidth": 80,
  //     "indentWidth": 2,
  //     "singleQuote": false,
  //     "proseWrap": "always"
  //   }
  // }
}

@tani
Copy link
Author

tani commented Feb 5, 2022

@jsejcksn Thank you for the suggestion! How about this script?

function generate(schema: any): any {
  if ("default" in schema) {
    return schema.default;
  }
  if ("type" in schema) {
    switch (schema.type) {
      case "object":
        return Object.fromEntries(
          Object.entries(schema.properties).map(([key, value]) => {
            return [key, generate(value)];
          }),
        );
      case "array":
        return [];
    }
  }
}

const schemaUrl =
  "https://deno.land/x/[email protected]/cli/schemas/config-file.v1.json";
const response = await fetch(schemaUrl);
const schema = await response.json();
const config = JSON.stringify(generate(schema), null, 2);
for(const line of config.split('\n')) {
  if (!line.includes('{}') && !line.includes('[]') && (line.match(/[{}\[\]]/))) {
    console.log(line)
  } else {
    console.log('//'+line)
  }
}

This script produces the JSONC as follows.

deno.jsonc
{
  "compilerOptions": {
//    "allowJs": true,
//    "allowUnreachableCode": false,
//    "allowUnusedLabels": false,
//    "checkJs": false,
//    "experimentalDecorators": true,
//    "jsx": "react",
//    "jsxFactory": "React.createElement",
//    "jsxFragmentFactory": "React.Fragment",
//    "jsxImportSource": "react",
//    "keyofStringsOnly": false,
    "lib": [
//      "deno.window"
    ],
//    "noFallthroughCasesInSwitch": false,
//    "noImplicitAny": true,
//    "noImplicitReturns": false,
//    "noImplicitThis": true,
//    "noImplicitUseStrict": true,
//    "noStrictGenericChecks": false,
//    "noUnusedLocals": false,
//    "noUnusedParameters": false,
//    "noUncheckedIndexedAccess": false,
//    "strict": true,
//    "strictBindCallApply": true,
//    "strictFunctionTypes": true,
//    "strictPropertyInitialization": true,
//    "strictNullChecks": true,
//    "suppressExcessPropertyErrors": false,
//    "suppressImplicitAnyIndexErrors": false
  },
  "lint": {
    "files": {
//      "include": [],
//      "exclude": []
    },
    "rules": {
//      "tags": [],
//      "exclude": [],
//      "include": []
    }
  },
  "fmt": {
    "files": {
//      "include": [],
//      "exclude": []
    },
    "options": {
//      "useTabs": false,
//      "lineWidth": 80,
//      "indentWidth": 2,
//      "singleQuote": false,
//      "proseWrap": "always"
    }
  }
}

@jsejcksn
Copy link
Contributor

jsejcksn commented Feb 5, 2022

How about this script?

@tani It's probably better to think about the actual implementation after there is consensus about the content and format. Here are some thoughts in regard to that:

  • All lines should be commented out within the JSON object (including the top-level properties)

  • Including descriptions would also be useful:

    Either:

    • An inline comment using the plaintext description:

      {
        // "compilerOptions": {
        //   "allowJs": true, // Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files.
        // }
      }
    • A JSDoc comment using the markdown description

      {
        // "compilerOptions": {
        //   /**
        //    * Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files.
        //    * 
        //    * See more: https://www.typescriptlang.org/tsconfig#allowJs
        //    */
        //   "allowJs": true,
        // }
      }

And getting the schema for the current version of Deno is obviously important:

`https://deno.land/x/deno@v${Deno.version.deno}/cli/schemas/config-file.v1.json`

@bartlomieju
Copy link
Member

"deno init" subcommand was added in #15469

@jsejcksn
Copy link
Contributor

@bartlomieju The possibility of generating a config file (as described in this issue) is mentioned in the PR, but it doesn't look like the PR actually included this feature. Since you closed this issue, is that a signal of "no interest to implement"?

@bartlomieju bartlomieju reopened this Aug 21, 2022
@bartlomieju
Copy link
Member

@bartlomieju The possibility of generating a config file (as described in this issue) is mentioned in the PR, but it doesn't look like the PR actually included this feature. Since you closed this issue, is that a signal of "no interest to implement"?

I've reopened the issue. For now we're shipping a first iteration of deno init that doesn't add any configurability. If you have use case for that please leave a comment or upvote.

@jsejcksn
Copy link
Contributor

I've reopened the issue.

@bartlomieju Thanks! Maybe we can change the title to reflect the more specific nature of this suggestion in comparison to what was merged.

@bartlomieju bartlomieju changed the title Add init command Add more features to "deno init" subcomand Aug 21, 2022
@bartlomieju bartlomieju changed the title Add more features to "deno init" subcomand Add more features to "deno init" subcommand Aug 21, 2022
@dragonwocky
Copy link

I'm not sure the deno init command as of Deno 1.25 does enough to warrant its name/existence? For example, npm init scaffolds a package name/version/entrypoint/etc. to generate something configurable that would actually be used/kept in a project built from that scaffold. deno init just generates a couple of example files with code that most likely wouldn't be kept for most projects, and that wouldn't be useful to developers with any past experience with Deno - it seems like a command that a users migrating from Node might try once and then never need again, more of a deno gen-example than a deno init.

A more useful deno init probably needs to scaffold out some basic configuration items - not necessarily for the compiler, it makes sense to favour the defaults for that, but for e.g. generating an import map and adding a project entrypoint as a deno task.

@andykais
Copy link

to expand upon @dragonwocky, I think that npm does something pretty useful, with the npm create svelte@latest, which as I understand it, simply runs a script in a package with create-<param>. I believe we can get a similar workflow in deno with deno init --script https://deno.land/x/oak/init.ts or something similar. I suppose this could be considered a different command too (like deno create https://deno.land/x/oak/init.ts), but either way this would be a nice normalizing of workflows for all the frameworks in deno (like oak, or even fresh, which just runs a generic command deno run -A -r https://fresh.deno.dev my-project)

@lucacasonato
Copy link
Member

I don't think this has anything actionable for us to do regarding deno init. If there are concrete suggestions for features we should add to deno init, please open separate issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
suggestion suggestions for new features (yet to be agreed) user feedback wanted feedback from the community is desired
Projects
None yet
Development

No branches or pull requests

8 participants