{"id":31006747,"url":"https://github.com/chrsm/yuecheck","last_synced_at":"2026-02-14T15:34:21.505Z","repository":{"id":314435353,"uuid":"1043473684","full_name":"chrsm/yuecheck","owner":"chrsm","description":"yuescript linter, formatter, and ast library","archived":false,"fork":false,"pushed_at":"2026-02-13T20:33:27.000Z","size":173,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"mother","last_synced_at":"2026-02-14T03:35:44.539Z","etag":null,"topics":["formatter","linter","yue","yuescript"],"latest_commit_sha":null,"homepage":"","language":"MoonScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrsm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-23T23:49:57.000Z","updated_at":"2026-02-13T20:33:30.000Z","dependencies_parsed_at":"2025-09-12T13:37:12.469Z","dependency_job_id":null,"html_url":"https://github.com/chrsm/yuecheck","commit_stats":null,"previous_names":["chrsm/yuecheck"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chrsm/yuecheck","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrsm%2Fyuecheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrsm%2Fyuecheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrsm%2Fyuecheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrsm%2Fyuecheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrsm","download_url":"https://codeload.github.com/chrsm/yuecheck/tar.gz/refs/heads/mother","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrsm%2Fyuecheck/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29448021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T14:10:32.461Z","status":"ssl_error","status_checked_at":"2026-02-14T14:09:49.945Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formatter","linter","yue","yuescript"],"created_at":"2025-09-13T02:36:47.041Z","updated_at":"2026-02-14T15:34:21.499Z","avatar_url":"https://github.com/chrsm.png","language":"MoonScript","funding_links":[],"categories":["Tools"],"sub_categories":["emacs"],"readme":"yuecheck\n===\n\nTools for writing [Yuescript][1].\n\n- linter (`yuecheck`)\n- formatter (`yuefmt`)\n\n`yuecheck` came about due to the fact that I love writing code in Yuescript, but\nthere aren't really any tools for it. It started as a standard linter I am attempting\nto expand that to a formatter (`yuefmt`).\n\n`yuefmt`, much like `yuecheck`, is based on my opinions of how code should be\nwritten and formatted. `yuecheck` is configurable at this point but `yuefmt` lacks\nthat. There may be things I will make configurable but ultimately it was a huge\nPITA to put together. If you have any suggestions, please feel free to file an\nissue.\n\n\n----\n\nCurrent TODOs:\n\n- [ ] General cleanup. Rules are ugly for implementation\n  * [x] categorized, but still more to do\n- [ ] More fleshed out LSP implementation. Unlikely this will be very advanced,\n  and less likely will it work with non-Yue code. Possibly could redirect to\n  luals.\n\n* [installation](#installation)\n* [yuecheck usage](#usage-of-yuecheck)\n* [yuefmt usage](#usage-of-yuefmt)\n* [configuration](#configuration)\n  * [enabling and disabling checks](#enabling-and-disabling-checks)\n* [built-in lint rules](#built-in-linters)\n* [writing custom rules](#writing-custom-rules)\n* [contributing](#contributing)\n* [example neovim setup](#example-neovim-setup)\n* [changelog](#changelog)\n\n\n# installation\n\nThere is a rockspec, but yuecheck is not yet available on luarocks. It is the\nrecommended way to install, however, to provide the `yuecheck` command.\n\n```bash\ngit clone git@github.com:chrsm/yuecheck.git\ncd yuecheck\nluarocks make\n# or luarocks --local make\n```\n\n# usage of yuecheck\n\nTo lint a specific file or directory, pass it as an argument to `yuecheck`,\nex `yuecheck .` (all yue files in cwd) or `yuecheck specific.yue` for a single file.\n\nSee below for other options.\n\n```\nUsage: yuecheck [-h] [--ignore-config] [-j]\n       ([-d \u003cdirectory\u003e] | [-f \u003cfile\u003e] | [\u003cdir_or_file\u003e] | [--stdin])\n       [-x [\u003cexclude\u003e] ...]\n\nOptions:\n   -h, --help            Show this help message and exit.\n            -d \u003cdirectory\u003e,\n   --directory \u003cdirectory\u003e\n                         directory containing files to check\n       -f \u003cfile\u003e,        specific file to check\n   --file \u003cfile\u003e\n   --stdin               parse stdin\n   --ignore-config       ignore repo or user config\n   -j, --json            output as json\n          -x [\u003cexclude\u003e] ...,\n   --exclude [\u003cexclude\u003e] ...\n                         pattern for paths to exclude\n\n```\n\nSimple example: `sample.yue`\n\n```moonscript\nx = \"abcd\"\n```\n\n```\n$ yuecheck sample.yue\nsample.yue:1:3: double-quoted string where single-quoted would suffice\n\n1 issues\n```\n\n\n# usage of yuefmt\n\nTo format a specific file, pass it as an argument to `yuefmt`, eg `yuefmt file.yue`.\nYou can also pass via `-f file.yue` or pump to `yuefmt` via stdin (`--stdin`).\n\nSee below for other options.\n\n```\nUsage: yuefmt [-h] [-w] ([-f \u003cfile\u003e] | [\u003cdir_or_file\u003e] | [--stdin])\n       [-x [\u003cexclude\u003e] ...]\n\ntool for formatting yue code\n\nArguments:\n   dir_or_file\n\nOptions:\n   -h, --help            Show this help message and exit.\n       -f \u003cfile\u003e,        file to format\n   --file \u003cfile\u003e\n   --stdin               parse stdin\n          -x [\u003cexclude\u003e] ...,\n   --exclude [\u003cexclude\u003e] ...\n                         pattern for paths to exclude\n   -w, --write           write changes to disk\n\nsee https://github.com/chrsm/yuecheck\n```\n\n\n\n## example neovim setup\n\nIf you're a neovim user, you can refer to my dotfiles for how I hooked this up via [nvim-lint][2]:\n[nvim_lint config][3].\n\n\n# configuration\n\nAll rules are enabled by default. For further customization, it is recommended\nto write a `.yuecheck` file with your desired settings. This file can be placed\nin two locations:\n\n- `(root dir with .git)/.yuecheck`\n  * TODO: make this so that yuecheck must trust it first, since it can run code.\n- `$HOME/.yuecheck`\n\nRepo-specific configuration files take precedence over the user-level file.\n\nThis file is a yue script itself, so you can write it however you prefer, so long\nas it returns a table matching the spec below. Note that this file is not\nrequired. Check the repo's `.yuecheck` file for a full example.\n\n```moonscript\nexport default {\n  -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n  -- NOTE! Set this to true only if you trust the code you're linting.\n  -- yuecheck currently doesn't certain things (const checks), and uses\n  -- the yue compiler to try to check for additional errors.\n  --\n  -- the code itself is not executed, but macros are run by the compiler,\n  -- and therefore side effects can occur.\n  --\n  -- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n  enable_compilation: false\n\n  -- if a rule is missing or set to false, it is disabled.\n  enabled_rules:\n    -- by default, all rules are set to enabled at runtime.\n    cond_identical_exprs: true\n    cond_impossible: true\n    unreachable: true\n    basic_nilness: true\n\n    global_writes: true\n    stdlib_usage: true\n    stdlib_match: true\n\n    style_comment_space: true\n    style_zero_index: true\n    style_import_as_ident: true\n    style_unnecessary_doublestring: true\n    style_conditionals: true\n    style_simplify_if_to_switch: true\n    style_discourage_require: true\n    style_discourage_unnecessary_sb: true\n\n  -- per-rule configuration, where possible\n  rules:\n    global_writes:\n      patterns: [ '^_ENV', '^package' ]\n}\n```\n\nAnother example - adding more checks to `stdlib_usage`.\n\n```moonscript\nimport 'yuecheck.rules'\n\ncustom_rules =\n  stdlib_usage:\n    definitions:\n      love:\n        filesystem:\n          append:\n            args:\n              * name: 'name', optional: false\n              * name: 'data', optional: false\n              * name: 'size', optional: true\n\n-- if you want to keep the current definitions, merge\nfor k, v in pairs rules.stdlib_usage.config.definitions\n  custom_rules.stdlib_usage.definitions[k] = v\n\ncfg =\n  rules: custom_rules\n\n  enabled_rules:\n    stdlib_usage: true\n\nexport default cfg\n```\n\n\n## enabling and disabling checks\n\n### per project\n\nConfiguration can be set up at the root of the repository. The config is Yuescript,\nso you can write it however you want. Some rules allow additional configuration.\n\nExample below.\n\n```moonscript\nexport default {\n  enabled_rules:\n    stdlib_usage: true\n    global_writes: false\n    cond_identical_exprs: true\n    cond_impossible: true\n    style_comment_space: true\n\n  rules:\n    global_writes:\n      patterns: { '^_ENV', '^package' }\n}\n```\n\n\n### per file\n\nLinters can be disabled by having a comment at the top of the file, even if\nthe config file or command line arguments enable them.\n\n```moonscript\n-- yuecheck:ignore=stdlib_usage,smell_global_writes\n\n_G['a'] = 'ignored'\nos.clock 'ignored'\n```\n\n```moonscript\n-- yuecheck:ignore\n\n_G['is also'] = 'acceptable'\n```\n\n\n### per line\n\n```moonscript\n_G['a'] = 'ignored' -- yuecheck:ignore\nos.clock 'not ignored'\n```\n\n\n# built-in lint rules\n\nMore to come, most likely. Most of these are style/preference, or ahead-of-time checks\nbefore running.\n\n| Rule | Description |\n| :---- | ----------- |\n| style_comment_space | Comments must have a space between `--` or `--[[` and content |\n| style_zero_index | Discourages use of t[0], as tables usually start at 1; Using 0 is likely unintentional. |\n| style_import_as_ident | Warns on redundant import name, i.e. `import 'x' as x` |\n| style_unnecessary_doublestring | Warns on unnecessary use of `\"` for strings where `'` is fine |\n| style_discourage_require | Discourages use of `require` in favor of `import` |\n| style_discourage_unnecessary_sb | Discourages use of square brackets in table literals, eg `{ ['a']: true }` can be `{ a: true }` |\n| ... | ... |\n| style_conditionals | Warns on odd conditionals, eg `if true`, `unless false` always execute |\n| style_simplify_if_switch | Warns on chain if-elseif that could be switch instead | \n| ... | ... |\n| stdlib_usage | Finds issues with standard library functions. Configurable. |\n| stdlib_match | Finds issues with patterns supplied to string.(match,find,gmatch,gsub) and capture groups |\n| ... | ... |\n| global_writes | Warns on writes to global variables (`_G`, `_ENV`, etc). Configurable. |\n| ... | ... |\n| cond_identical_exprs | Warns on conditions that compare a value to itself |\n| cond_impossible | Warns on some types of impossible conditions |\n| unreachable | Warns on code that is unreachable |\n| basic_nilness | Warns on certain types of nil checks that aren't necessary |\n| nil_comparisons | Warns about syntax errors from conditions against nil |\n\n\n# writing custom rules\n\nCustom checks can be added to the linter very easily. The best way to add one\nis by adding it to your `.yuecheck` file.\n\n```moonscript\nimport 'yuecheck.types'\nimport 'yuecheck.linter'\n\nconfig =\n  enabled_rules:\n    my_check: true\n\nmy_check =\n  type: 'wat'\n  name: 'my_check'\n  on:\n    * types.Value\n  check: (node) -\u003e\n    unless some_condition node\n      return\n\n    {\n      type: 'WARN'\n      message: \"value found\"\n      line: node\\g_src_line!\n      col: node\\g_src_col!\n    }\n\nlinter.define_rule my_check\n\nexport default config\n```\n\nReturn values from a check should be:\n```moonscript\n{\n  type: string HINT|INFO|WARN|ERROR\n  message: string\n  line: int\n  col: int\n}\n-- or a table of tables\n{\n  result1, result2, ...\n}\n```\n\nWhile `type` string value is not enforced, it is useful to be consistent for\nintegrations with other tools.\n\nNOTE:\n\nCurrently there is some work in making rules easier to declare, such that\nif you are looking for specific things, there will be an easier way to\nretrieve information than looking at the full node tree everywhere.\n\nYou can look at `src/yuecheck/rules.yue` `stdlib_match`, which uses the premade\n`FuncRule`.\n\n\n# important notes\n\n## AST simplification\n\n`yue.to_ast` even with flatten=0 does simplify some parts, requiring specific\nhandling within `linter.build_ast`.\n\n- `TableLit` with empty `.values` is returned with `{ }`\n- `FnArgDefList` will have an empty '' when `f = () -\u003e` is used instead of `f = -\u003e`\n- `Return` will have a 'return' when `f = -\u003e return` (vs `f = -\u003e`)\n- `DefaultValue` will have '' sometimes (I didn't document why, sorry!)\n- `DoubleString` sometimes has '' instead of `DoubleString\u003eDoubleStringContent\u003eDoubleStringInner ''`\n- `x^2` `^` is lost, resulting in only `Callable + Value`, unlike other operators\n- `r[#r]` is returned as literal `[#r]` instead of `ReversedIndex`.\n\nThere are likely more simplifications that are not listed. I am adding cases as I find them.\n\n\n## type definitions\n\n`src/types.yue` is a generated file. To regenerate it, run `make generate`.\n\nThis calls two scripts:\n- `bin/fetch_ast.yue`\n  * fetches `yue_ast.h` header from [Yuescript][1] repo\n  * does some simple parsing of types\n  * has a few manual overrides because I'm lazy and haven't made it parse better\n  * generates `gen/types_raw.yue` (ignored in .gitignore)\n- `bin/generate_types.yue`\n  * imports `gen/types_raw.yue` and generates `src/types.yue`\n  * has all types specified as actual `class` instances\n  * allows walking ast and comparing types\n\n\n## comments\n\nComments from `yue.to_ast` are only preserved on `Statement`.\n\n```moonscript\n-- a\n-- b\nc = 1\n-- .comments[] { '-- a', '-- b' }\n```\n\nComments that are not a immediately preceding a statement are lost as expected.\n```moonscript\n-- a\n-- b\n\nprint 1 -- .comments[] {}\n\nprint 1 -- comment\n--^ .comments[] {} \u003c- not present, too\n```\n\nMultiline comments swallow all surrounding whitespace.\nAnd despite `Statement\u003c.comments\u003cYueLineComment,YueMultilineComment\u003e\u003e`, `to_ast`\nsimplifies to `YueLineComment|MultilineCommentInner`.\n\n```moonscript\n-- a\n-- b\n--[[ c ]]\nd = 1\n-- .comments[] { LineComment'-- a', LineComment'-- b', MultilineComment'c' }\n\n-- same result regardless of whitespace\n-- [[\n  c\n]]\n```\n\nWhile this library does handle constructing these when they are added to\nstatements, it also has a crude comment 'parser' itself that maps lines\nto comments.\n\n```moonscript\nsrc = \"-- comment1\\nb = 1 -- comment 2\\n\\n-- comment 3\\n...etc\"\n\n-- returns t{ indices: { line# that has comment, ... }, [line#]: { YueLineComment|YueMultilineComment, ... }\n-- indices are useful if you want to just know which lines have comments or iter, as table holes will prevent\n-- full iteration.\ncomments = linter.find_comments src \n\nfor idx in *comments.indices\n  for v in *comments[idx]\n    -- depending on YueMultilineComment or YueLineComment val\n    print v.inner?.v ?? v.v\n```\n\n\n# contributing\n\n- **don't be an ass**\n- write decent commit messages\n\n\n[1]: https://github.com/IppClub/YueScript\n[2]: https://github.com/mfussenegger/nvim-lint\n[3]: https://github.com/chrsm/dotfiles/blob/master/neovim/.config/nvim/lua/plug/nvim_lint.yue#L14\n\n\n# changelog\n\n2026-02\n  * implemented `yuefmt`, a tool for formatting yue code\n  * WONTFIX'd .yuecheck comp protection; so little use of yue that this seems unnecessary and annoying\n\n2026-01\n  * updated to YueScript 0.32.4; possible breaking changes\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrsm%2Fyuecheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrsm%2Fyuecheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrsm%2Fyuecheck/lists"}