yamllint-js 0.2.0 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.2](https://github.com/kimzuni-labs/yamllint-js/compare/v0.2.1...v0.2.2) (2026-02-19)
4
+
5
+
6
+ ### 🐛 Bug Fixes
7
+
8
+ * detection yamllint field in package.json ([#37](https://github.com/kimzuni-labs/yamllint-js/issues/37)) ([5fe02d7](https://github.com/kimzuni-labs/yamllint-js/commit/5fe02d73c299bba7a53edb90a56aa16fad4fc845))
9
+
10
+ ## [0.2.1](https://github.com/kimzuni-labs/yamllint-js/compare/v0.2.0...v0.2.1) (2026-02-14)
11
+
12
+
13
+ ### ✨ Features
14
+
15
+ * export detectUserGlobalConfig function ([#33](https://github.com/kimzuni-labs/yamllint-js/issues/33)) ([7dd1643](https://github.com/kimzuni-labs/yamllint-js/commit/7dd1643a09cb5c131d13decca0945beebd219b9d))
16
+ * export loadYamlLintConfig function ([#35](https://github.com/kimzuni-labs/yamllint-js/issues/35)) ([e0b0738](https://github.com/kimzuni-labs/yamllint-js/commit/e0b073858adb2a2b43af297584cd709628bbd7f9))
17
+
3
18
  ## [0.2.0](https://github.com/kimzuni-labs/yamllint-js/compare/v0.1.0...v0.2.0) (2026-02-14)
4
19
 
5
20
 
package/README.md CHANGED
@@ -130,14 +130,15 @@ In addition to the yamllint configuration format,
130
130
  JavaScript and TypeScript configuration files are also supported:
131
131
 
132
132
  - [yamllint configuration files and environment variables](https://yamllint.readthedocs.io/en/stable/configuration.html):
133
- - [Cosmiconfig default search places](https://github.com/cosmiconfig/cosmiconfig/blob/a5a842547c13392ebb89a485b9e56d9f37e3cbd3/src/defaults.ts#L12-L32):
134
- + `package.json` (`"yamllint-js"` field), `yamllint-js.config.ts`, etc. (Not support `rc` files)
135
- + `package.json` (`"yamllint"` field), `.yamllintrc.json`, `yamllint.config.ts`, etc.
133
+ - `package.json` or js/ts config files
134
+ + `package.json` (`"yamllint-js"`, `"yamllint"` fields)
135
+ + `yamllint-js.config.js`, `.ts`, `.cjs`, `.mjs`
136
+ + `yamllint.config.js`, `.ts`, `.cjs`, `.mjs`
136
137
 
137
138
  Configuration can be easily defined with type hints, like:
138
139
 
139
140
  ```typescript
140
- /** @type {import("yamllint-js").UserConfig")} */
141
+ /** @type {import("yamllint-js").UserConfig} */
141
142
 
142
143
  const config = {/* ... */};
143
144
 
@@ -220,3 +221,4 @@ rules:
220
221
  ## License
221
222
 
222
223
  [GPL version 3](./LICENSE)
224
+ (port of [yamllint](https://github.com/adrienverge/yamllint))
package/dist/cli.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { APP, LEVELS, PROBLEM_LEVELS } from "./constants.mjs";
2
- import { getHomedir } from "./utils.mjs";
3
2
  import { run as run$1 } from "./linter.mjs";
4
- import { YamlLintConfig, loadConfigFile } from "./config.mjs";
3
+ import { YamlLintConfig, loadYamlLintConfig } from "./config.mjs";
5
4
  import fs from "node:fs/promises";
6
5
  import path from "node:path";
7
6
  import os from "node:os";
@@ -168,10 +167,6 @@ async function run(argv = hideBin(process.argv), stdin = process.stdin) {
168
167
  process.exitCode = -1;
169
168
  return;
170
169
  }
171
- let userGlobalConfig;
172
- if (process.env.YAMLLINT_CONFIG_FILE !== void 0) userGlobalConfig = process.env.YAMLLINT_CONFIG_FILE;
173
- else if (process.env.XDG_CONFIG_HOME !== void 0) userGlobalConfig = path.join(process.env.XDG_CONFIG_HOME, "yamllint", "config");
174
- else userGlobalConfig = path.join(getHomedir(), ".config", "yamllint", "config");
175
170
  function getValue(value, type = "string") {
176
171
  if (type === "boolean") return !!value;
177
172
  const values = Array.isArray(value) ? value : [value];
@@ -187,12 +182,7 @@ async function run(argv = hideBin(process.argv), stdin = process.stdin) {
187
182
  if (configData !== "" && !configData.includes(":")) configData = `extends: ${configData}`;
188
183
  conf = await YamlLintConfig.init({ content: configData });
189
184
  } else if (configFile !== void 0) conf = await YamlLintConfig.init({ file: configFile });
190
- else {
191
- const load = await loadConfigFile();
192
- if (load !== null) conf = await YamlLintConfig.init({ _data: load.config });
193
- else if (await fs.stat(userGlobalConfig).then((x) => x.isFile()).catch(() => false)) conf = await YamlLintConfig.init({ file: userGlobalConfig });
194
- else conf = await YamlLintConfig.init({ content: "extends: default" });
195
- }
185
+ else conf = await loadYamlLintConfig();
196
186
  } catch (e) {
197
187
  console.error(String(e));
198
188
  process.exitCode = -1;
package/dist/config.d.mts CHANGED
@@ -2,7 +2,6 @@ import { Level } from "./constants.mjs";
2
2
  import { Rule, RuleConf } from "./rules/types.mjs";
3
3
  import { RuleId } from "./rules/index.mjs";
4
4
  import { AllLevel, BuiltInExtendName, MaybeCamelCaseKeys, Prettify, ToCamelCaseKeys } from "./types.mjs";
5
- import * as cosmiconfig0 from "cosmiconfig";
6
5
  import { Ignore } from "ignore";
7
6
 
8
7
  //#region src/config.d.ts
@@ -70,6 +69,10 @@ declare class YamlLintConfig {
70
69
  enabledRules(filepath?: string): Rule[];
71
70
  extend(baseConfig: unknown): void;
72
71
  }
72
+ interface LoadConfigFileOptions {
73
+ startDir?: string;
74
+ stopDir?: string;
75
+ }
73
76
  /**
74
77
  * Load YAML lint configuration from a file.
75
78
  *
@@ -80,7 +83,21 @@ declare class YamlLintConfig {
80
83
  * const specified = await loadConfigFile("path/to/yamllint.yaml");
81
84
  * ```
82
85
  */
83
- declare const loadConfigFile: (filepath?: string) => Promise<cosmiconfig0.CosmiconfigResult>;
86
+ declare const loadConfigFile: (filepath?: string | LoadConfigFileOptions) => Promise<unknown>;
87
+ /**
88
+ * Detect user global config file path.
89
+ *
90
+ * @example
91
+ *
92
+ * ```typescript
93
+ * await detectUserGlobalConfig(); // "/home/user/.config/yamllint/config"
94
+ * ```
95
+ */
96
+ declare function detectUserGlobalConfig(): Promise<string | undefined>;
97
+ /**
98
+ * Load and return a fully resolved YamlLint configuration instance.
99
+ */
100
+ declare function loadYamlLintConfig(): Promise<YamlLintConfig>;
84
101
  /**
85
102
  * if value is {@link AllLevel} or undefined
86
103
  * then convert to {@link Level} (undefined -> "error"),
@@ -103,4 +120,4 @@ declare const loadConfigFile: (filepath?: string) => Promise<cosmiconfig0.Cosmic
103
120
  */
104
121
  declare function validateLevel(value: unknown): Level | undefined;
105
122
  //#endregion
106
- export { CamelCaseConfigData, KebabCaseConfigData, MaybeCamelCaseConfigData, YamlLintConfig, YamlLintConfigError, YamlLintConfigProps, loadConfigFile, validateLevel };
123
+ export { CamelCaseConfigData, KebabCaseConfigData, MaybeCamelCaseConfigData, YamlLintConfig, YamlLintConfigError, YamlLintConfigProps, detectUserGlobalConfig, loadConfigFile, loadYamlLintConfig, validateLevel };
package/dist/config.mjs CHANGED
@@ -1,14 +1,14 @@
1
- import { ALIASES, APP, CONFIG_SEARCH_PLACES, LEVELS, YAML_OPTIONS } from "./constants.mjs";
2
- import { formatErrorMessage, getHomedir, splitlines, toKebabCaseKeys } from "./utils.mjs";
1
+ import { ALIASES, COMMAND_NAMES, LEVELS, PY_YAMLLINT_CONFIG_FILES, YAML_OPTIONS } from "./constants.mjs";
2
+ import { formatErrorMessage, getHomedir, getNodeSearchPlaces, splitlines, toKebabCaseKeys } from "./utils.mjs";
3
3
  import { autoDecode, linesInFiles } from "./decoder.mjs";
4
4
  import { get } from "./rules/index.mjs";
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
7
- import { cosmiconfig, defaultLoaders } from "cosmiconfig";
8
7
  import assert from "node:assert";
9
8
  import yaml from "yaml";
10
9
  import z from "zod";
11
10
  import ignore$1 from "ignore";
11
+ import { createJiti } from "jiti";
12
12
  /*!
13
13
  * Copyright (C) 2016 Adrien Vergé
14
14
  * Copyright (C) 2025 kimzuni
@@ -79,7 +79,7 @@ var YamlLintConfig = class YamlLintConfig {
79
79
  if (props._data !== void 0 || props.data !== void 0) this.#data = props._data ?? props.data;
80
80
  else try {
81
81
  if (props.content !== void 0) this.#data = yaml.parse(props.content, YAML_OPTIONS);
82
- else this.#data = await loadConfigFile(props.file).then((x) => x?.config);
82
+ else this.#data = await loadConfigFile(props.file);
83
83
  } catch (e) {
84
84
  throw new YamlLintConfigError(formatErrorMessage("invalid config: ", e));
85
85
  }
@@ -165,26 +165,72 @@ async function validateRuleConf(rule, config) {
165
165
  return conf;
166
166
  }
167
167
  const loadConfigFile = (() => {
168
- const loadYAML = async function loadYAML(filepath) {
169
- const content = autoDecode(await fs.readFile(filepath));
170
- return yaml.parse(content, YAML_OPTIONS);
171
- };
172
- const explorer = cosmiconfig(APP.NAME, {
173
- cache: false,
174
- stopDir: getHomedir(),
175
- searchPlaces: CONFIG_SEARCH_PLACES,
176
- loaders: {
177
- ...defaultLoaders,
178
- ".json": loadYAML,
179
- ".yaml": loadYAML,
180
- ".yml": loadYAML,
181
- noExt: loadYAML
168
+ const jsReg = /\.[cm]?js$/;
169
+ const homeDir = getHomedir();
170
+ const filenames = [
171
+ "package.json",
172
+ ...getNodeSearchPlaces(COMMAND_NAMES),
173
+ ...PY_YAMLLINT_CONFIG_FILES
174
+ ];
175
+ const loadFile = async (filepath, throwOnFailure = false) => {
176
+ try {
177
+ filepath = path.resolve(filepath);
178
+ const filename = path.basename(filepath);
179
+ if (jsReg.test(filepath) || filepath.endsWith(".ts")) {
180
+ const jiti = createJiti(import.meta.url);
181
+ if (await fs.stat(filepath).then((x) => x.size).catch(() => 0) < 1) throw new Error();
182
+ const mod = await jiti.import(filepath, { default: true });
183
+ const value = mod.default ?? mod;
184
+ if (typeof value === "object" && value !== null) return value;
185
+ } else if (filename === "package.json") {
186
+ const content = autoDecode(await fs.readFile(filepath));
187
+ const pkg = JSON.parse(content);
188
+ if (typeof pkg !== "object" || pkg === null) return;
189
+ for (const name of COMMAND_NAMES) {
190
+ const value = pkg[name];
191
+ if (value) return value;
192
+ }
193
+ } else {
194
+ const content = autoDecode(await fs.readFile(filepath));
195
+ return yaml.parse(content, YAML_OPTIONS);
196
+ }
197
+ } catch {
198
+ if (throwOnFailure) throw new YamlLintConfigError(`failed to load config file "${filepath}"`);
199
+ return;
182
200
  }
183
- });
184
- return function loadConfigFile(filepath) {
185
- return filepath === void 0 ? explorer.search() : explorer.load(filepath);
201
+ };
202
+ return async function loadConfigFile(filepath) {
203
+ if (typeof filepath === "string") return loadFile(filepath, true);
204
+ const { startDir = process.cwd(), stopDir } = filepath ?? {};
205
+ let currDir = path.resolve(startDir);
206
+ do {
207
+ for (const filename of filenames) {
208
+ const curr = path.join(currDir, filename);
209
+ if (await fs.stat(curr).then((x) => x.isFile() || x.isSymbolicLink()).catch(() => false)) {
210
+ const content = await loadFile(curr);
211
+ if (content) return content;
212
+ }
213
+ }
214
+ currDir = path.dirname(currDir);
215
+ } while (currDir !== stopDir && currDir !== homeDir && currDir !== path.dirname(currDir));
186
216
  };
187
217
  })();
218
+ async function detectUserGlobalConfig() {
219
+ let userGlobalConfig;
220
+ if (process.env.YAMLLINT_CONFIG_FILE !== void 0) userGlobalConfig = process.env.YAMLLINT_CONFIG_FILE;
221
+ else if (process.env.XDG_CONFIG_HOME !== void 0) userGlobalConfig = path.join(process.env.XDG_CONFIG_HOME, "yamllint", "config");
222
+ else userGlobalConfig = path.join(getHomedir(), ".config", "yamllint", "config");
223
+ return await fs.stat(userGlobalConfig).then((x) => x.isFile()).catch(() => false) ? userGlobalConfig : void 0;
224
+ }
225
+ async function loadYamlLintConfig() {
226
+ let userGlobalConfig;
227
+ let load;
228
+ let conf;
229
+ if (load = await loadConfigFile()) conf = await YamlLintConfig.init({ _data: load });
230
+ else if (userGlobalConfig = await detectUserGlobalConfig()) conf = await YamlLintConfig.init({ file: userGlobalConfig });
231
+ else conf = await YamlLintConfig.init({ content: "extends: default" });
232
+ return conf;
233
+ }
188
234
  async function getExtendedConfigFile(name) {
189
235
  if (!name.includes("/")) {
190
236
  const stdConf = path.join(import.meta.dirname, "conf", `${name}.yaml`);
@@ -225,4 +271,4 @@ function zodIssueDetect(issue) {
225
271
  if (issue.code === "invalid_type") return ["should be ", issue.expected];
226
272
  return ["unknown error"];
227
273
  }
228
- export { YamlLintConfig, YamlLintConfigError, loadConfigFile, validateLevel };
274
+ export { YamlLintConfig, YamlLintConfigError, detectUserGlobalConfig, loadConfigFile, loadYamlLintConfig, validateLevel };
@@ -1,5 +1,5 @@
1
- import { description, name, version } from "./package.mjs";
2
- import { getDefaultSearchPlaces } from "cosmiconfig";
1
+ import { bin, description, name, version } from "./package.mjs";
2
+ const COMMAND_NAMES = Object.keys(bin);
3
3
  const APP = {
4
4
  NAME: name,
5
5
  VERSION: version,
@@ -10,17 +10,15 @@ const APP = {
10
10
  "such as lines length, trailing spaces, indentation, etc."
11
11
  ].join(" ")
12
12
  };
13
- const YAML_OPTIONS = {
14
- version: "1.1",
15
- uniqueKeys: false
16
- };
17
- const CONFIG_SEARCH_PLACES = [
18
- ...getDefaultSearchPlaces(APP.NAME).filter((x) => !x.includes(`${APP.NAME}rc`)),
19
- ...getDefaultSearchPlaces("yamllint").filter((x) => !x.includes("yamllintrc")),
13
+ const PY_YAMLLINT_CONFIG_FILES = [
20
14
  ".yamllint",
21
15
  ".yamllint.yaml",
22
16
  ".yamllint.yml"
23
17
  ];
18
+ const YAML_OPTIONS = {
19
+ version: "1.1",
20
+ uniqueKeys: false
21
+ };
24
22
  const LEVELS = [
25
23
  null,
26
24
  "warning",
@@ -40,4 +38,4 @@ const PROBLEM_LEVELS = {
40
38
  };
41
39
  const PY_EOL = /\r\n|\r|\n|\v|\f|\x1c|\x1d|\x1e|\x85|\u2028|\u2029/;
42
40
  const PY_EOL_END = new RegExp(`(${PY_EOL.source})$`);
43
- export { ALIASES, APP, CONFIG_SEARCH_PLACES, LEVELS, PROBLEM_LEVELS, PY_EOL, PY_EOL_END, YAML_OPTIONS };
41
+ export { ALIASES, APP, COMMAND_NAMES, LEVELS, PROBLEM_LEVELS, PY_EOL, PY_EOL_END, PY_YAMLLINT_CONFIG_FILES, YAML_OPTIONS };
@@ -1,6 +1,6 @@
1
1
  import { ALIASES, Alias, LEVELS, Level } from "./constants.mjs";
2
2
  import { LintProblem, run } from "./linter.mjs";
3
3
  import { RuleId } from "./rules/index.mjs";
4
- import { CamelCaseConfigData, KebabCaseConfigData, MaybeCamelCaseConfigData, YamlLintConfig, YamlLintConfigError, YamlLintConfigProps, loadConfigFile, validateLevel } from "./config.mjs";
4
+ import { CamelCaseConfigData, KebabCaseConfigData, MaybeCamelCaseConfigData, YamlLintConfig, YamlLintConfigError, YamlLintConfigProps, detectUserGlobalConfig, loadConfigFile, loadYamlLintConfig, validateLevel } from "./config.mjs";
5
5
  import { AllLevel, BuiltInExtendName, Prettify } from "./types.mjs";
6
- export { ALIASES, type Alias, type AllLevel, type BuiltInExtendName, type CamelCaseConfigData, type KebabCaseConfigData, LEVELS, type Level, LintProblem, type MaybeCamelCaseConfigData, type Prettify, type RuleId, YamlLintConfig, YamlLintConfigError, type YamlLintConfigProps, run as linter, loadConfigFile, validateLevel };
6
+ export { ALIASES, type Alias, type AllLevel, type BuiltInExtendName, type CamelCaseConfigData, type KebabCaseConfigData, LEVELS, type Level, LintProblem, type MaybeCamelCaseConfigData, type Prettify, type RuleId, YamlLintConfig, YamlLintConfigError, type YamlLintConfigProps, detectUserGlobalConfig, run as linter, loadConfigFile, loadYamlLintConfig, validateLevel };
package/dist/internal.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import { ALIASES, LEVELS } from "./constants.mjs";
2
2
  import { LintProblem, run } from "./linter.mjs";
3
- import { YamlLintConfig, YamlLintConfigError, loadConfigFile, validateLevel } from "./config.mjs";
4
- export { ALIASES, LEVELS, LintProblem, YamlLintConfig, YamlLintConfigError, run as linter, loadConfigFile, validateLevel };
3
+ import { YamlLintConfig, YamlLintConfigError, detectUserGlobalConfig, loadConfigFile, loadYamlLintConfig, validateLevel } from "./config.mjs";
4
+ export { ALIASES, LEVELS, LintProblem, YamlLintConfig, YamlLintConfigError, detectUserGlobalConfig, run as linter, loadConfigFile, loadYamlLintConfig, validateLevel };
package/dist/package.mjs CHANGED
@@ -1,4 +1,8 @@
1
1
  var name = "yamllint-js";
2
2
  var description = "A linter for YAML files — an unofficial native Node.js port of Python yamllint.";
3
- var version = "0.2.0";
4
- export { description, name, version };
3
+ var version = "0.2.2";
4
+ var bin = {
5
+ "yamllint-js": "dist/main.mjs",
6
+ "yamllint": "dist/main.mjs"
7
+ };
8
+ export { bin, description, name, version };
package/dist/utils.mjs CHANGED
@@ -49,4 +49,14 @@ const toKebabCaseKeys = (data) => {
49
49
  for (const key in data) newData[toKebabCase(key)] = data[key];
50
50
  return newData;
51
51
  };
52
- export { B, bufferStartsWith, formatErrorMessage, getHomedir, once, splitlines, toKebabCaseKeys };
52
+ const getNodeSearchPlaces = (name, set = /* @__PURE__ */ new Set()) => {
53
+ const names = Array.isArray(name) ? name : [name];
54
+ for (const n of names) {
55
+ set.add(`${n}.config.js`);
56
+ set.add(`${n}.config.ts`);
57
+ set.add(`${n}.config.cjs`);
58
+ set.add(`${n}.config.mjs`);
59
+ }
60
+ return set;
61
+ };
62
+ export { B, bufferStartsWith, formatErrorMessage, getHomedir, getNodeSearchPlaces, once, splitlines, toKebabCaseKeys };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yamllint-js",
3
3
  "description": "A linter for YAML files — an unofficial native Node.js port of Python yamllint.",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "type": "module",
7
7
  "author": {
@@ -23,8 +23,10 @@
23
23
  "lint:eslint": "eslint .",
24
24
  "lint:types": "tsc --noEmit",
25
25
  "lint:markdown": "markdownlint-cli2",
26
- "lint:yaml": "node dist/main.mjs -c yamllint.self.config.js . 2> /dev/null || tsx src/main.ts -c yamllint.self.config.js .",
27
- "build": "tsdown"
26
+ "lint:yaml": "node dist/main.mjs -c yamllint.self.config.js .",
27
+ "prelint:yaml": "npm run build:nodts",
28
+ "build": "tsdown",
29
+ "build:nodts": "tsdown --no-dts"
28
30
  },
29
31
  "files": [
30
32
  "dist",
@@ -51,9 +53,9 @@
51
53
  "./internal": "./dist/internal.mjs"
52
54
  },
53
55
  "dependencies": {
54
- "cosmiconfig": "^9.0.0",
55
56
  "iconv-lite": "^0.7.2",
56
57
  "ignore": "^7.0.5",
58
+ "jiti": "^2.6.1",
57
59
  "recheck": "^4.5.0",
58
60
  "yaml": "^2.8.2",
59
61
  "yargs": "^18.0.0",
@@ -67,11 +69,9 @@
67
69
  "@vitest/coverage-v8": "^4.0.18",
68
70
  "@vitest/ui": "^4.0.18",
69
71
  "eslint": "^9.39.2",
70
- "jiti": "^2.6.1",
71
72
  "markdownlint-cli2": "^0.20.0",
72
73
  "standard-version": "^9.5.0",
73
74
  "tsdown": "^0.20.1",
74
- "tsx": "^4.21.0",
75
75
  "typescript": "^5.9.3",
76
76
  "typescript-eslint": "^8.54.0",
77
77
  "vitest": "^4.0.18"