diff --git a/packages/core/__tests__/config/load-config.test.ts b/packages/core/__tests__/config/load-config.test.ts index ddc2e2d2a..f279cbfdc 100644 --- a/packages/core/__tests__/config/load-config.test.ts +++ b/packages/core/__tests__/config/load-config.test.ts @@ -1,8 +1,9 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; -import { describe, beforeEach, afterEach, test, expect } from 'vitest'; +import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest'; import { loadConfig } from '../../src/config/index.js'; import { normalizePath } from '../../src/config/config.js'; +import { require } from '../../src/config/loader.js'; describe('Config: loadConfig', () => { const testDir = `${os.tmpdir()}/glint-config-test-load-config-${process.pid}`; @@ -51,4 +52,49 @@ describe('Config: loadConfig', () => { expect(config.environment.getConfiguredTemplateTags()).toEqual({ test: {} }); expect(config.checkStandaloneTemplates).toBe(false); }); + + test('locates config in package', () => { + const directory = `${testDir}/package-glint-config`; + const nodeModulePackageDir = `${directory}/node_modules/@package1`; + + vi.spyOn(require, 'resolve').mockImplementation((id: string | undefined) => { + if (id === '@package1/tsconfig.json') { + return id.replace('@package1', nodeModulePackageDir); + } + throw Error(`Cannot resolve module ${id}`); + }); + + fs.mkdirSync(nodeModulePackageDir, { recursive: true }); + fs.writeFileSync( + `${nodeModulePackageDir}/package.json`, + JSON.stringify({ + name: '@package1', + version: '1.0.0', + }), + ); + fs.writeFileSync( + `${nodeModulePackageDir}/tsconfig.json`, + JSON.stringify({ + glint: { + environment: 'kaboom', + checkStandaloneTemplates: false, + }, + }), + ); + fs.writeFileSync( + `${directory}/tsconfig.json`, + JSON.stringify({ + extends: '@package1/tsconfig.json', + glint: { + environment: '../local-env', + }, + }), + ); + + let config = loadConfig(`${directory}`); + + expect(config.rootDir).toBe(normalizePath(`${directory}`)); + expect(config.environment.getConfiguredTemplateTags()).toEqual({ test: {} }); + expect(config.checkStandaloneTemplates).toBe(false); + }); }); diff --git a/packages/core/src/config/loader.ts b/packages/core/src/config/loader.ts index 8f1ad479c..48893dab8 100644 --- a/packages/core/src/config/loader.ts +++ b/packages/core/src/config/loader.ts @@ -1,11 +1,17 @@ import { createRequire } from 'node:module'; import * as path from 'node:path'; +import * as fs from 'node:fs'; import SilentError from 'silent-error'; import { GlintConfig } from './config.js'; import { GlintConfigInput } from '@glint/core/config-types'; import type TS from 'typescript'; -const require = createRequire(import.meta.url); +/** + * @private + * + * Only exported for testing purposes. Do not import. + */ +export const require = createRequire(import.meta.url); type TypeScript = typeof TS; @@ -75,8 +81,15 @@ function loadConfigInput(ts: TypeScript, entryPath: string): GlintConfigInput | ); fullGlintConfig = { ...currentGlintConfig, ...fullGlintConfig }; - currentPath = - currentContents.extends && path.resolve(path.dirname(currentPath), currentContents.extends); + + if (currentContents.extends) { + currentPath = path.resolve(path.dirname(currentPath), currentContents.extends); + if (!fs.existsSync(currentPath)) { + currentPath = require.resolve(currentContents.extends); + } + } else { + currentPath = undefined; + } } return validateConfigInput(fullGlintConfig);