|
| 1 | +import fs from "node:fs"; |
| 2 | +import path from "node:path"; |
| 3 | +// eslint-disable-next-line import/no-unresolved |
| 4 | +import { globalIgnores } from "eslint/config"; |
| 5 | +import semver from "semver"; |
| 6 | +import configs from "./configs/index.js"; |
| 7 | +import ignorePaths from "./ignore-paths.js"; |
| 8 | + |
| 9 | +const SKIP_TIME = 5000; |
| 10 | + |
| 11 | +class Cache { |
| 12 | + /** |
| 13 | + * Initialize this cache instance. |
| 14 | + */ |
| 15 | + constructor() { |
| 16 | + this.map = new Map(); |
| 17 | + } |
| 18 | + |
| 19 | + /** |
| 20 | + * Get the cached value of the given key. |
| 21 | + * @param {string} key The key to get. |
| 22 | + * @returns {import('type-fest').JsonObject} The cached value or null. |
| 23 | + */ |
| 24 | + get(key) { |
| 25 | + const entry = this.map.get(key); |
| 26 | + const now = Date.now(); |
| 27 | + |
| 28 | + if (entry) { |
| 29 | + if (entry.expire > now) { |
| 30 | + entry.expire = now + SKIP_TIME; |
| 31 | + return entry.value; |
| 32 | + } |
| 33 | + this.map.delete(key); |
| 34 | + } |
| 35 | + return null; |
| 36 | + } |
| 37 | + |
| 38 | + /** |
| 39 | + * Set the value of the given key. |
| 40 | + * @param {string} key The key to set. |
| 41 | + * @param {import('type-fest').JsonObject} value The value to set. |
| 42 | + * @returns {void} |
| 43 | + */ |
| 44 | + set(key, value) { |
| 45 | + const entry = this.map.get(key); |
| 46 | + const expire = Date.now() + SKIP_TIME; |
| 47 | + |
| 48 | + if (entry) { |
| 49 | + entry.value = value; |
| 50 | + entry.expire = expire; |
| 51 | + } else { |
| 52 | + this.map.set(key, { value, expire }); |
| 53 | + } |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +const cache = new Cache(); |
| 58 | + |
| 59 | +/** |
| 60 | + * Reads the `package.json` data in a given path. |
| 61 | + * |
| 62 | + * Don't cache the data. |
| 63 | + * @param {string} dir The path to a directory to read. |
| 64 | + * @returns {import('type-fest').JsonObject|null} The read `package.json` data, or null. |
| 65 | + */ |
| 66 | +function readPackageJson(dir) { |
| 67 | + const filePath = path.join(dir, "package.json"); |
| 68 | + try { |
| 69 | + const text = fs.readFileSync(filePath, "utf8"); |
| 70 | + const data = JSON.parse(text); |
| 71 | + |
| 72 | + if ( |
| 73 | + data !== null && |
| 74 | + typeof data === "object" && |
| 75 | + Array.isArray(data) === false |
| 76 | + ) { |
| 77 | + data.filePath = filePath; |
| 78 | + return data; |
| 79 | + } |
| 80 | + // eslint-disable-next-line unicorn/prefer-optional-catch-binding |
| 81 | + } catch (_err) { |
| 82 | + // do nothing. |
| 83 | + } |
| 84 | + |
| 85 | + return null; |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * Gets a `package.json` data. |
| 90 | + * The data is cached if found, then it's used after. |
| 91 | + * @param {string=} startPath A file path to lookup. |
| 92 | + * @returns {import('type-fest').JsonObject | null} A found `package.json` data or `null`. |
| 93 | + * This object have additional property `filePath`. |
| 94 | + */ |
| 95 | +function getPackageJson(startPath = "a.js") { |
| 96 | + const startDir = path.dirname(path.resolve(startPath)); |
| 97 | + let dir = startDir; |
| 98 | + let prevDir = ""; |
| 99 | + let data = null; |
| 100 | + |
| 101 | + do { |
| 102 | + data = cache.get(dir); |
| 103 | + if (data) { |
| 104 | + if (dir !== startDir) { |
| 105 | + cache.set(startDir, data); |
| 106 | + } |
| 107 | + return data; |
| 108 | + } |
| 109 | + |
| 110 | + data = readPackageJson(dir); |
| 111 | + if (data) { |
| 112 | + cache.set(dir, data); |
| 113 | + cache.set(startDir, data); |
| 114 | + return data; |
| 115 | + } |
| 116 | + |
| 117 | + // Go to next. |
| 118 | + prevDir = dir; |
| 119 | + dir = path.resolve(dir, ".."); |
| 120 | + } while (dir !== prevDir); |
| 121 | + |
| 122 | + cache.set(startDir, null); |
| 123 | + return null; |
| 124 | +} |
| 125 | + |
| 126 | +const packageJson = getPackageJson(); |
| 127 | +const isModule = |
| 128 | + packageJson !== null && |
| 129 | + typeof packageJson === "object" && |
| 130 | + "type" in packageJson && |
| 131 | + packageJson.type === "module"; |
| 132 | + |
| 133 | +/** |
| 134 | + * @returns {Record<string, string>} javascript configuration |
| 135 | + */ |
| 136 | +function getJavascriptConfig() { |
| 137 | + if (packageJson.engines && packageJson.engines.node) { |
| 138 | + const minVersion = semver.minVersion(packageJson.engines.node).major; |
| 139 | + |
| 140 | + // https://node.green/ |
| 141 | + switch (minVersion) { |
| 142 | + case 8: |
| 143 | + case 9: |
| 144 | + return configs["javascript/es2017"]; |
| 145 | + case 10: |
| 146 | + case 11: |
| 147 | + return configs["javascript/es2018"]; |
| 148 | + case 12: |
| 149 | + case 13: { |
| 150 | + const languageOptions = { |
| 151 | + ...configs["javascript/es2019"].languageOptions, |
| 152 | + }; |
| 153 | + |
| 154 | + languageOptions.globals.Promise = false; |
| 155 | + languageOptions.globals.BigInt = false; |
| 156 | + |
| 157 | + return { ...configs["javascript/es2019"], languageOptions }; |
| 158 | + } |
| 159 | + case 14: |
| 160 | + return configs["javascript/es2020"]; |
| 161 | + |
| 162 | + case 15: |
| 163 | + return configs["javascript/es2021"]; |
| 164 | + case 16: |
| 165 | + case 17: |
| 166 | + case 18: |
| 167 | + case 19: |
| 168 | + return configs["javascript/es2022"]; |
| 169 | + case 20: |
| 170 | + case 21: |
| 171 | + return configs["javascript/es2023"]; |
| 172 | + case 22: |
| 173 | + case 23: |
| 174 | + return configs["javascript/es2024"]; |
| 175 | + case 24: |
| 176 | + case 25: |
| 177 | + return configs["javascript/es2025"]; |
| 178 | + default: |
| 179 | + return configs["javascript/recommended"]; |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + return configs["javascript/recommended"]; |
| 184 | +} |
| 185 | + |
| 186 | +configs.recommended = [ |
| 187 | + globalIgnores(ignorePaths), |
| 188 | + isModule |
| 189 | + ? configs["node/mixed-module-and-commonjs"] |
| 190 | + : configs["node/mixed-commonjs-and-module"], |
| 191 | + getJavascriptConfig(), |
| 192 | + configs["typescript/jsdoc"], |
| 193 | + configs["jest/recommended"], |
| 194 | + configs["markdown/recommended"], |
| 195 | + configs["stylistic/recommended"], |
| 196 | +]; |
| 197 | + |
| 198 | +configs["recommended-dirty"] = [ |
| 199 | + globalIgnores(ignorePaths), |
| 200 | + configs["node/mixed-dirty"], |
| 201 | + getJavascriptConfig(), |
| 202 | + configs["typescript/jsdoc"], |
| 203 | + configs["jest/recommended"], |
| 204 | + configs["markdown/recommended"], |
| 205 | + configs["stylistic/recommended"], |
| 206 | +]; |
| 207 | + |
| 208 | +export { default } from "./configs/index.js"; |
0 commit comments