| 1 | import {
|
|---|
| 2 | loadTsconfig,
|
|---|
| 3 | tsConfigLoader,
|
|---|
| 4 | walkForTsConfig,
|
|---|
| 5 | } from "../tsconfig-loader";
|
|---|
| 6 | import { join } from "path";
|
|---|
| 7 |
|
|---|
| 8 | describe("tsconfig-loader", () => {
|
|---|
| 9 | it("should find tsconfig in cwd", () => {
|
|---|
| 10 | const result = tsConfigLoader({
|
|---|
| 11 | cwd: "/foo/bar",
|
|---|
| 12 | getEnv: (_: string) => undefined,
|
|---|
| 13 | loadSync: (cwd: string) => {
|
|---|
| 14 | return {
|
|---|
| 15 | tsConfigPath: `${cwd}/tsconfig.json`,
|
|---|
| 16 | baseUrl: "./",
|
|---|
| 17 | paths: {},
|
|---|
| 18 | };
|
|---|
| 19 | },
|
|---|
| 20 | });
|
|---|
| 21 |
|
|---|
| 22 | // assert.equal(result.tsConfigPath, "/foo/bar/tsconfig.json");
|
|---|
| 23 | expect(result.tsConfigPath).toBe("/foo/bar/tsconfig.json");
|
|---|
| 24 | });
|
|---|
| 25 |
|
|---|
| 26 | it("should return loaderResult.tsConfigPath as undefined when not found", () => {
|
|---|
| 27 | const result = tsConfigLoader({
|
|---|
| 28 | cwd: "/foo/bar",
|
|---|
| 29 | getEnv: (_: string) => undefined,
|
|---|
| 30 | loadSync: (_: string) => {
|
|---|
| 31 | return {
|
|---|
| 32 | tsConfigPath: undefined,
|
|---|
| 33 | baseUrl: "./",
|
|---|
| 34 | paths: {},
|
|---|
| 35 | };
|
|---|
| 36 | },
|
|---|
| 37 | });
|
|---|
| 38 |
|
|---|
| 39 | // assert.isUndefined(result.tsConfigPath);
|
|---|
| 40 | expect(result.tsConfigPath).toBeUndefined();
|
|---|
| 41 | });
|
|---|
| 42 |
|
|---|
| 43 | it("should use TS_NODE_PROJECT env if exists", () => {
|
|---|
| 44 | const result = tsConfigLoader({
|
|---|
| 45 | cwd: "/foo/bar",
|
|---|
| 46 | getEnv: (key: string) =>
|
|---|
| 47 | key === "TS_NODE_PROJECT" ? "/foo/baz" : undefined,
|
|---|
| 48 | loadSync: (cwd: string, fileName: string) => {
|
|---|
| 49 | if (cwd === "/foo/bar" && fileName === "/foo/baz") {
|
|---|
| 50 | return {
|
|---|
| 51 | tsConfigPath: "/foo/baz/tsconfig.json",
|
|---|
| 52 | baseUrl: "./",
|
|---|
| 53 | paths: {},
|
|---|
| 54 | };
|
|---|
| 55 | }
|
|---|
| 56 |
|
|---|
| 57 | return {
|
|---|
| 58 | tsConfigPath: undefined,
|
|---|
| 59 | baseUrl: "./",
|
|---|
| 60 | paths: {},
|
|---|
| 61 | };
|
|---|
| 62 | },
|
|---|
| 63 | });
|
|---|
| 64 |
|
|---|
| 65 | // assert.equal(result.tsConfigPath, "/foo/baz/tsconfig.json");
|
|---|
| 66 | expect(result.tsConfigPath).toBe("/foo/baz/tsconfig.json");
|
|---|
| 67 | });
|
|---|
| 68 |
|
|---|
| 69 | it("should use TS_NODE_BASEURL env if exists", () => {
|
|---|
| 70 | const result = tsConfigLoader({
|
|---|
| 71 | cwd: "/foo/bar",
|
|---|
| 72 | getEnv: (key: string) =>
|
|---|
| 73 | key === "TS_NODE_BASEURL" ? "SOME_BASEURL" : undefined,
|
|---|
| 74 | loadSync: (_0: string, _1: string, baseUrl: string) => {
|
|---|
| 75 | return {
|
|---|
| 76 | tsConfigPath: undefined,
|
|---|
| 77 | baseUrl,
|
|---|
| 78 | paths: {},
|
|---|
| 79 | };
|
|---|
| 80 | },
|
|---|
| 81 | });
|
|---|
| 82 |
|
|---|
| 83 | // assert.equal(result.baseUrl, "SOME_BASEURL");
|
|---|
| 84 | expect(result.baseUrl).toBe("SOME_BASEURL");
|
|---|
| 85 | });
|
|---|
| 86 |
|
|---|
| 87 | it("should not use TS_NODE_BASEURL env if it does not exist", () => {
|
|---|
| 88 | const result = tsConfigLoader({
|
|---|
| 89 | cwd: "/foo/bar",
|
|---|
| 90 | getEnv: (_: string) => {
|
|---|
| 91 | return undefined;
|
|---|
| 92 | },
|
|---|
| 93 | loadSync: (_0: string, _1: string, baseUrl: string) => {
|
|---|
| 94 | return {
|
|---|
| 95 | tsConfigPath: undefined,
|
|---|
| 96 | baseUrl,
|
|---|
| 97 | paths: {},
|
|---|
| 98 | };
|
|---|
| 99 | },
|
|---|
| 100 | });
|
|---|
| 101 |
|
|---|
| 102 | // assert.equal(result.baseUrl, undefined);
|
|---|
| 103 | expect(result.baseUrl).toBeUndefined();
|
|---|
| 104 | });
|
|---|
| 105 | });
|
|---|
| 106 |
|
|---|
| 107 | describe("walkForTsConfig", () => {
|
|---|
| 108 | it("should find tsconfig in starting directory", () => {
|
|---|
| 109 | const pathToTsconfig = join("/root", "dir1", "tsconfig.json");
|
|---|
| 110 | const res = walkForTsConfig(
|
|---|
| 111 | join("/root", "dir1"),
|
|---|
| 112 | (path) => path === pathToTsconfig
|
|---|
| 113 | );
|
|---|
| 114 | // assert.equal(res, pathToTsconfig);
|
|---|
| 115 | expect(res).toBe(pathToTsconfig);
|
|---|
| 116 | });
|
|---|
| 117 |
|
|---|
| 118 | it("should find tsconfig in parent directory", () => {
|
|---|
| 119 | const pathToTsconfig = join("/root", "tsconfig.json");
|
|---|
| 120 | const res = walkForTsConfig(
|
|---|
| 121 | join("/root", "dir1"),
|
|---|
| 122 | (path) => path === pathToTsconfig
|
|---|
| 123 | );
|
|---|
| 124 | // assert.equal(res, pathToTsconfig);
|
|---|
| 125 | expect(res).toBe(pathToTsconfig);
|
|---|
| 126 | });
|
|---|
| 127 |
|
|---|
| 128 | it("should return undefined when reaching the top", () => {
|
|---|
| 129 | const res = walkForTsConfig(join("/root", "dir1", "kalle"), () => false);
|
|---|
| 130 | // assert.equal(res, undefined);
|
|---|
| 131 | expect(res).toBeUndefined();
|
|---|
| 132 | });
|
|---|
| 133 | });
|
|---|
| 134 |
|
|---|
| 135 | describe("loadConfig", () => {
|
|---|
| 136 | it("should load a config", () => {
|
|---|
| 137 | const config = { compilerOptions: { baseUrl: "hej" } };
|
|---|
| 138 | const res = loadTsconfig(
|
|---|
| 139 | "/root/dir1/tsconfig.json",
|
|---|
| 140 | (path) => path === "/root/dir1/tsconfig.json",
|
|---|
| 141 | (_) => JSON.stringify(config)
|
|---|
| 142 | );
|
|---|
| 143 | // assert.deepEqual(res, config);
|
|---|
| 144 | expect(res).toStrictEqual(config);
|
|---|
| 145 | });
|
|---|
| 146 |
|
|---|
| 147 | it("should load a config with comments", () => {
|
|---|
| 148 | const config = { compilerOptions: { baseUrl: "hej" } };
|
|---|
| 149 | const res = loadTsconfig(
|
|---|
| 150 | "/root/dir1/tsconfig.json",
|
|---|
| 151 | (path) => path === "/root/dir1/tsconfig.json",
|
|---|
| 152 | (_) => `{
|
|---|
| 153 | // my comment
|
|---|
| 154 | "compilerOptions": {
|
|---|
| 155 | "baseUrl": "hej"
|
|---|
| 156 | }
|
|---|
| 157 | }`
|
|---|
| 158 | );
|
|---|
| 159 | // assert.deepEqual(res, config);
|
|---|
| 160 | expect(res).toStrictEqual(config);
|
|---|
| 161 | });
|
|---|
| 162 |
|
|---|
| 163 | it("should load a config with trailing commas", () => {
|
|---|
| 164 | const config = { compilerOptions: { baseUrl: "hej" } };
|
|---|
| 165 | const res = loadTsconfig(
|
|---|
| 166 | "/root/dir1/tsconfig.json",
|
|---|
| 167 | (path) => path === "/root/dir1/tsconfig.json",
|
|---|
| 168 | (_) => `{
|
|---|
| 169 | "compilerOptions": {
|
|---|
| 170 | "baseUrl": "hej",
|
|---|
| 171 | },
|
|---|
| 172 | }`
|
|---|
| 173 | );
|
|---|
| 174 | // assert.deepEqual(res, config);
|
|---|
| 175 | expect(res).toStrictEqual(config);
|
|---|
| 176 | });
|
|---|
| 177 |
|
|---|
| 178 | it("should throw an error including the file path when encountering invalid JSON5", () => {
|
|---|
| 179 | expect(() =>
|
|---|
| 180 | loadTsconfig(
|
|---|
| 181 | "/root/dir1/tsconfig.json",
|
|---|
| 182 | (path) => path === "/root/dir1/tsconfig.json",
|
|---|
| 183 | (_) => `{
|
|---|
| 184 | "compilerOptions": {
|
|---|
| 185 | }`
|
|---|
| 186 | )
|
|---|
| 187 | ).toThrowError(
|
|---|
| 188 | "/root/dir1/tsconfig.json is malformed JSON5: invalid end of input at 3:12"
|
|---|
| 189 | );
|
|---|
| 190 | });
|
|---|
| 191 |
|
|---|
| 192 | it("should load a config with string extends and overwrite all options", () => {
|
|---|
| 193 | const firstConfig = {
|
|---|
| 194 | extends: "../base-config.json",
|
|---|
| 195 | compilerOptions: { baseUrl: "kalle", paths: { foo: ["bar2"] } },
|
|---|
| 196 | };
|
|---|
| 197 | const firstConfigPath = join("/root", "dir1", "tsconfig.json");
|
|---|
| 198 | const baseConfig = {
|
|---|
| 199 | compilerOptions: {
|
|---|
| 200 | baseUrl: "olle",
|
|---|
| 201 | paths: { foo: ["bar1"] },
|
|---|
| 202 | strict: true,
|
|---|
| 203 | },
|
|---|
| 204 | };
|
|---|
| 205 | const baseConfigPath = join("/root", "base-config.json");
|
|---|
| 206 | const res = loadTsconfig(
|
|---|
| 207 | join("/root", "dir1", "tsconfig.json"),
|
|---|
| 208 | (path) => path === firstConfigPath || path === baseConfigPath,
|
|---|
| 209 | (path) => {
|
|---|
| 210 | if (path === firstConfigPath) {
|
|---|
| 211 | return JSON.stringify(firstConfig);
|
|---|
| 212 | }
|
|---|
| 213 | if (path === baseConfigPath) {
|
|---|
| 214 | return JSON.stringify(baseConfig);
|
|---|
| 215 | }
|
|---|
| 216 | return "";
|
|---|
| 217 | }
|
|---|
| 218 | );
|
|---|
| 219 |
|
|---|
| 220 | // assert.deepEqual(res, {
|
|---|
| 221 | // extends: "../base-config.json",
|
|---|
| 222 | // compilerOptions: {
|
|---|
| 223 | // baseUrl: "kalle",
|
|---|
| 224 | // paths: { foo: ["bar2"] },
|
|---|
| 225 | // strict: true,
|
|---|
| 226 | // },
|
|---|
| 227 | // });
|
|---|
| 228 | expect(res).toEqual({
|
|---|
| 229 | extends: "../base-config.json",
|
|---|
| 230 | compilerOptions: {
|
|---|
| 231 | baseUrl: "kalle",
|
|---|
| 232 | paths: { foo: ["bar2"] },
|
|---|
| 233 | strict: true,
|
|---|
| 234 | },
|
|---|
| 235 | });
|
|---|
| 236 | });
|
|---|
| 237 |
|
|---|
| 238 | it("should load a config with string extends from node_modules and overwrite all options", () => {
|
|---|
| 239 | const firstConfig = {
|
|---|
| 240 | extends: "my-package/base-config.json",
|
|---|
| 241 | compilerOptions: { baseUrl: "kalle", paths: { foo: ["bar2"] } },
|
|---|
| 242 | };
|
|---|
| 243 | const firstConfigPath = join("/root", "dir1", "tsconfig.json");
|
|---|
| 244 | const baseConfig = {
|
|---|
| 245 | compilerOptions: {
|
|---|
| 246 | baseUrl: "olle",
|
|---|
| 247 | paths: { foo: ["bar1"] },
|
|---|
| 248 | strict: true,
|
|---|
| 249 | },
|
|---|
| 250 | };
|
|---|
| 251 | const baseConfigPath = join(
|
|---|
| 252 | "/root",
|
|---|
| 253 | "dir1",
|
|---|
| 254 | "node_modules",
|
|---|
| 255 | "my-package",
|
|---|
| 256 | "base-config.json"
|
|---|
| 257 | );
|
|---|
| 258 | const res = loadTsconfig(
|
|---|
| 259 | join("/root", "dir1", "tsconfig.json"),
|
|---|
| 260 | (path) => path === firstConfigPath || path === baseConfigPath,
|
|---|
| 261 | (path) => {
|
|---|
| 262 | if (path === firstConfigPath) {
|
|---|
| 263 | return JSON.stringify(firstConfig);
|
|---|
| 264 | }
|
|---|
| 265 | if (path === baseConfigPath) {
|
|---|
| 266 | return JSON.stringify(baseConfig);
|
|---|
| 267 | }
|
|---|
| 268 | return "";
|
|---|
| 269 | }
|
|---|
| 270 | );
|
|---|
| 271 |
|
|---|
| 272 | // assert.deepEqual(res, {
|
|---|
| 273 | // extends: "my-package/base-config.json",
|
|---|
| 274 | // compilerOptions: {
|
|---|
| 275 | // baseUrl: "kalle",
|
|---|
| 276 | // paths: { foo: ["bar2"] },
|
|---|
| 277 | // strict: true,
|
|---|
| 278 | // },
|
|---|
| 279 | // });
|
|---|
| 280 | expect(res).toEqual({
|
|---|
| 281 | extends: "my-package/base-config.json",
|
|---|
| 282 | compilerOptions: {
|
|---|
| 283 | baseUrl: "kalle",
|
|---|
| 284 | paths: { foo: ["bar2"] },
|
|---|
| 285 | strict: true,
|
|---|
| 286 | },
|
|---|
| 287 | });
|
|---|
| 288 | });
|
|---|
| 289 |
|
|---|
| 290 | it("should use baseUrl relative to location of extended tsconfig", () => {
|
|---|
| 291 | const firstConfig = { compilerOptions: { baseUrl: "." } };
|
|---|
| 292 | const firstConfigPath = join("/root", "first-config.json");
|
|---|
| 293 | const secondConfig = { extends: "../first-config.json" };
|
|---|
| 294 | const secondConfigPath = join("/root", "dir1", "second-config.json");
|
|---|
| 295 | const thirdConfig = { extends: "../second-config.json" };
|
|---|
| 296 | const thirdConfigPath = join("/root", "dir1", "dir2", "third-config.json");
|
|---|
| 297 | const res = loadTsconfig(
|
|---|
| 298 | join("/root", "dir1", "dir2", "third-config.json"),
|
|---|
| 299 | (path) =>
|
|---|
| 300 | path === firstConfigPath ||
|
|---|
| 301 | path === secondConfigPath ||
|
|---|
| 302 | path === thirdConfigPath,
|
|---|
| 303 | (path) => {
|
|---|
| 304 | if (path === firstConfigPath) {
|
|---|
| 305 | return JSON.stringify(firstConfig);
|
|---|
| 306 | }
|
|---|
| 307 | if (path === secondConfigPath) {
|
|---|
| 308 | return JSON.stringify(secondConfig);
|
|---|
| 309 | }
|
|---|
| 310 | if (path === thirdConfigPath) {
|
|---|
| 311 | return JSON.stringify(thirdConfig);
|
|---|
| 312 | }
|
|---|
| 313 | return "";
|
|---|
| 314 | }
|
|---|
| 315 | );
|
|---|
| 316 |
|
|---|
| 317 | // assert.deepEqual(res, {
|
|---|
| 318 | // extends: "../second-config.json",
|
|---|
| 319 | // compilerOptions: { baseUrl: join("..", "..") },
|
|---|
| 320 | // });
|
|---|
| 321 | expect(res).toEqual({
|
|---|
| 322 | extends: "../second-config.json",
|
|---|
| 323 | compilerOptions: { baseUrl: join("..", "..") },
|
|---|
| 324 | });
|
|---|
| 325 | });
|
|---|
| 326 |
|
|---|
| 327 | it("should load a config with array extends and overwrite all options", () => {
|
|---|
| 328 | const baseConfig1 = {
|
|---|
| 329 | compilerOptions: { baseUrl: ".", paths: { foo: ["bar"] } },
|
|---|
| 330 | };
|
|---|
| 331 | const baseConfig1Path = join("/root", "base-config-1.json");
|
|---|
| 332 | const baseConfig2 = { compilerOptions: { baseUrl: "." } };
|
|---|
| 333 | const baseConfig2Path = join("/root", "dir1", "base-config-2.json");
|
|---|
| 334 | const baseConfig3 = {
|
|---|
| 335 | compilerOptions: { baseUrl: ".", paths: { foo: ["bar2"] } },
|
|---|
| 336 | };
|
|---|
| 337 | const baseConfig3Path = join("/root", "dir1", "dir2", "base-config-3.json");
|
|---|
| 338 | const actualConfig = {
|
|---|
| 339 | extends: [
|
|---|
| 340 | "./base-config-1.json",
|
|---|
| 341 | "./dir1/base-config-2.json",
|
|---|
| 342 | "./dir1/dir2/base-config-3.json",
|
|---|
| 343 | ],
|
|---|
| 344 | };
|
|---|
| 345 | const actualConfigPath = join("/root", "tsconfig.json");
|
|---|
| 346 |
|
|---|
| 347 | const res = loadTsconfig(
|
|---|
| 348 | join("/root", "tsconfig.json"),
|
|---|
| 349 | (path) =>
|
|---|
| 350 | [
|
|---|
| 351 | baseConfig1Path,
|
|---|
| 352 | baseConfig2Path,
|
|---|
| 353 | baseConfig3Path,
|
|---|
| 354 | actualConfigPath,
|
|---|
| 355 | ].indexOf(path) >= 0,
|
|---|
| 356 | (path) => {
|
|---|
| 357 | if (path === baseConfig1Path) {
|
|---|
| 358 | return JSON.stringify(baseConfig1);
|
|---|
| 359 | }
|
|---|
| 360 | if (path === baseConfig2Path) {
|
|---|
| 361 | return JSON.stringify(baseConfig2);
|
|---|
| 362 | }
|
|---|
| 363 | if (path === baseConfig3Path) {
|
|---|
| 364 | return JSON.stringify(baseConfig3);
|
|---|
| 365 | }
|
|---|
| 366 | if (path === actualConfigPath) {
|
|---|
| 367 | return JSON.stringify(actualConfig);
|
|---|
| 368 | }
|
|---|
| 369 | return "";
|
|---|
| 370 | }
|
|---|
| 371 | );
|
|---|
| 372 |
|
|---|
| 373 | expect(res).toEqual({
|
|---|
| 374 | extends: [
|
|---|
| 375 | "./base-config-1.json",
|
|---|
| 376 | "./dir1/base-config-2.json",
|
|---|
| 377 | "./dir1/dir2/base-config-3.json",
|
|---|
| 378 | ],
|
|---|
| 379 | compilerOptions: {
|
|---|
| 380 | baseUrl: join("dir1", "dir2"),
|
|---|
| 381 | paths: { foo: ["bar2"] },
|
|---|
| 382 | },
|
|---|
| 383 | });
|
|---|
| 384 | });
|
|---|
| 385 |
|
|---|
| 386 | it("should load a config with array extends without .json extension", () => {
|
|---|
| 387 | const baseConfig = {
|
|---|
| 388 | compilerOptions: { baseUrl: ".", paths: { foo: ["bar"] } },
|
|---|
| 389 | };
|
|---|
| 390 | const baseConfigPath = join("/root", "base-config-1.json");
|
|---|
| 391 | const actualConfig = { extends: ["./base-config-1"] };
|
|---|
| 392 | const actualConfigPath = join("/root", "tsconfig.json");
|
|---|
| 393 |
|
|---|
| 394 | const res = loadTsconfig(
|
|---|
| 395 | join("/root", "tsconfig.json"),
|
|---|
| 396 | (path) => [baseConfigPath, actualConfigPath].indexOf(path) >= 0,
|
|---|
| 397 | (path) => {
|
|---|
| 398 | if (path === baseConfigPath) {
|
|---|
| 399 | return JSON.stringify(baseConfig);
|
|---|
| 400 | }
|
|---|
| 401 | if (path === actualConfigPath) {
|
|---|
| 402 | return JSON.stringify(actualConfig);
|
|---|
| 403 | }
|
|---|
| 404 | return "";
|
|---|
| 405 | }
|
|---|
| 406 | );
|
|---|
| 407 |
|
|---|
| 408 | expect(res).toEqual({
|
|---|
| 409 | extends: ["./base-config-1"],
|
|---|
| 410 | compilerOptions: {
|
|---|
| 411 | baseUrl: ".",
|
|---|
| 412 | paths: { foo: ["bar"] },
|
|---|
| 413 | },
|
|---|
| 414 | });
|
|---|
| 415 | });
|
|---|
| 416 | });
|
|---|