-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(RootScan): ensure RootScan also scans
module
fields in package…
….json (#369) * feat(RootScan): ensure RootScan also scans `module` fields in package.json * docs: add copilot generated tsdoc * refactor: process review and cleanup code
- Loading branch information
Showing
15 changed files
with
1,098 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,108 @@ | ||
import { readFileSync } from 'fs'; | ||
import { dirname, join } from 'path'; | ||
|
||
/** | ||
* Represents a partial package.json object. | ||
*/ | ||
type PartialPackageJson = Partial<{ | ||
main: string; | ||
module: string; | ||
type: 'commonjs' | 'module'; | ||
}>; | ||
|
||
/** | ||
* Represents the root data. | ||
*/ | ||
export interface RootData { | ||
/** | ||
* The root directory. | ||
*/ | ||
root: string; | ||
|
||
/** | ||
* The type of the module system used. | ||
* It can be either 'ESM' or 'CommonJS'. | ||
*/ | ||
type: 'ESM' | 'CommonJS'; | ||
} | ||
|
||
let data: RootData | null = null; | ||
|
||
/** | ||
* Returns the directory name of a given path by joining the current working directory (cwd) with the joinable path. | ||
* @private | ||
* @param cwd - The current working directory. | ||
* @param joinablePath - The path to be joined with the cwd. | ||
* @returns The directory name of the joined path. | ||
*/ | ||
function dirnameWithPath(cwd: string, joinablePath: string) { | ||
return dirname(join(cwd, joinablePath)); | ||
} | ||
|
||
export function getRootData(): RootData { | ||
if (data !== null) return data; | ||
return (data ??= parseRootData()); | ||
} | ||
|
||
/** | ||
* Retrieves the root data of the project. | ||
* | ||
* This function reads the `package.json` file in the current working directory and determines the root path and type | ||
* of the project. | ||
* | ||
* - If the `package.json` file is not found or cannot be parsed, it assumes the project is using CommonJS and | ||
* the current working directory is used as the root | ||
* | ||
* - If the project `type` is specified as `"commonjs"` or `"module"` in the `package.json`, it uses the corresponding | ||
* `main` or `module` file path as the root. | ||
* | ||
* - If there is no `main` or `module` then it uses the current working directory as the root, while retaining the | ||
* matching `CommonJS` or `ESM` based on the `type` | ||
* | ||
* - If the main or module file path is not specified, it uses the current working directory as the root. | ||
* | ||
* The following table shows how different situations resolve to different root data | ||
* | ||
* | fields | resolved as | | ||
* |--------------------------|-------------| | ||
* | type=commonjs && main | CommonJS | | ||
* | type=commonjs && module | CommonJS | | ||
* | type=module && main | ESM | | ||
* | type=module && module | ESM | | ||
* | type=undefined && main | CommonJS | | ||
* | type=undefined && module | ESM | | ||
* | no package.json on cwd | CommonJS | | ||
* | ||
* @returns The root data object containing the root path and the type of the project. | ||
*/ | ||
export function parseRootData(): RootData { | ||
const cwd = process.cwd(); | ||
|
||
let file: PartialPackageJson | undefined; | ||
|
||
try { | ||
const file = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')); | ||
data = { root: dirname(join(cwd, file.main)), type: file.type === 'module' ? 'ESM' : 'CommonJS' }; | ||
} catch { | ||
data = { root: cwd, type: 'CommonJS' }; | ||
file = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')) as PartialPackageJson; | ||
} catch (error) { | ||
return { root: cwd, type: 'CommonJS' }; | ||
} | ||
|
||
return data; | ||
} | ||
const { main: packageMain, module: packageModule, type: packageType } = file; | ||
|
||
export interface RootData { | ||
root: string; | ||
type: 'ESM' | 'CommonJS'; | ||
const lowerCasedType = packageType?.toLowerCase() as PartialPackageJson['type']; | ||
|
||
if (lowerCasedType === 'commonjs') { | ||
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'CommonJS' }; | ||
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'CommonJS' }; | ||
return { root: cwd, type: 'CommonJS' }; | ||
} | ||
|
||
if (lowerCasedType === 'module') { | ||
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'ESM' }; | ||
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'ESM' }; | ||
return { root: cwd, type: 'ESM' }; | ||
} | ||
|
||
if (packageMain) return { root: dirnameWithPath(cwd, packageMain), type: 'CommonJS' }; | ||
if (packageModule) return { root: dirnameWithPath(cwd, packageModule), type: 'ESM' }; | ||
|
||
return { root: cwd, type: 'CommonJS' }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"type": "commonjs", | ||
"main": "dist/lib/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"type": "commonjs", | ||
"module": "dist/lib/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"type": "module", | ||
"main": "dist/lib/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"type": "module", | ||
"module": "dist/lib/index.js" | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"main": "dist/lib/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"module": "dist/lib/index.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { join } from 'node:path'; | ||
import type { MockInstance } from 'vitest'; | ||
import { parseRootData, type RootData } from '../../../src/lib/internal/RootScan'; | ||
|
||
let cwd: MockInstance<[], string>; | ||
|
||
afterEach(() => { | ||
expect(cwd).toHaveBeenCalled(); | ||
|
||
cwd.mockRestore(); | ||
}); | ||
|
||
describe('RootScan', () => { | ||
describe('type=commonjs', () => { | ||
const commonJsBasePath = 'commonjs'; | ||
|
||
test('GIVEN type=commonjs && main property THEN returns correct root', () => { | ||
mockCwd(commonJsBasePath, 'main'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'CommonJS', commonJsBasePath, 'main', 'dist', 'lib'); | ||
}); | ||
|
||
test('GIVEN type=commonjs && module property THEN returns correct root', () => { | ||
mockCwd(commonJsBasePath, 'module'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'CommonJS', commonJsBasePath, 'module', 'dist', 'lib'); | ||
}); | ||
}); | ||
|
||
describe('type=module', () => { | ||
const moduleBasePath = 'module'; | ||
|
||
test('GIVEN type=module && main property THEN returns correct root', () => { | ||
mockCwd(moduleBasePath, 'main'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'ESM', moduleBasePath, 'main', 'dist', 'lib'); | ||
}); | ||
|
||
test('GIVEN type=module && module property THEN returns correct root', () => { | ||
mockCwd(moduleBasePath, 'module'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'ESM', moduleBasePath, 'module', 'dist', 'lib'); | ||
}); | ||
}); | ||
|
||
describe('no-package', () => { | ||
const noPackageBasePath = 'no-package'; | ||
|
||
test('GIVEN no package.json THEN returns correct root', () => { | ||
mockCwd(noPackageBasePath); | ||
|
||
const rootData = parseRootData(); | ||
|
||
expect(rootData).toStrictEqual<RootData>({ | ||
root: testsFixturesJoin(noPackageBasePath), | ||
type: 'CommonJS' | ||
}); | ||
}); | ||
}); | ||
|
||
describe('type=undefined', () => { | ||
const noTypeBasePath = 'no-type'; | ||
|
||
test('GIVEN type=undefined && main property THEN returns correct root', () => { | ||
mockCwd(noTypeBasePath, 'main'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'CommonJS', noTypeBasePath, 'main', 'dist', 'lib'); | ||
}); | ||
|
||
test('GIVEN type=undefined && module property THEN returns correct root', () => { | ||
mockCwd(noTypeBasePath, 'module'); | ||
|
||
const rootData = parseRootData(); | ||
expectRootDataToEqual(rootData, 'ESM', noTypeBasePath, 'module', 'dist', 'lib'); | ||
}); | ||
}); | ||
}); | ||
|
||
/** | ||
* Mocks the current working directory by setting the return value of `process.cwd()` to the joined path of the provided arguments. | ||
* | ||
* @param pathForCwd - The path segments to join and set as the current working directory. | ||
*/ | ||
function mockCwd(...pathForCwd: string[]) { | ||
cwd = vi.spyOn(process, 'cwd').mockReturnValue(testsFixturesJoin(...pathForCwd)); | ||
} | ||
|
||
/** | ||
* Asserts that the actual RootData object is equal to the expected RootData object. | ||
* @param actual - The actual RootData object. | ||
* @param type - The type of the RootData object ('ESM' or 'CommonJS'). | ||
* @param rootPath - The root path of the RootData object. | ||
*/ | ||
function expectRootDataToEqual(actual: RootData, type: 'ESM' | 'CommonJS', ...rootPath: string[]) { | ||
expect(actual).toStrictEqual<RootData>({ | ||
root: testsFixturesJoin(...rootPath), | ||
type | ||
}); | ||
} | ||
|
||
/** | ||
* Joins the given path segments with 'tests/fixtures' and returns the resulting path. | ||
* | ||
* @param path - The path segments to join. | ||
* @returns The joined path. | ||
*/ | ||
function testsFixturesJoin(...path: string[]) { | ||
return join('tests', 'fixtures', ...path); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"extends": "../tsconfig.base.json", | ||
"compilerOptions": { | ||
"types": ["vitest/globals"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
{ | ||
"extends": "./tsconfig.base.json", | ||
"include": ["src", "scripts", "tsup.config.ts"] | ||
"compilerOptions": { | ||
"types": ["vitest/globals"], | ||
"lib": ["DOM", "ESNext"] | ||
}, | ||
"include": ["src", "tests", "scripts", "vitest.config.ts", "tsup.config.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { defineConfig } from 'vitest/config'; | ||
|
||
export default defineConfig({ | ||
test: { | ||
globals: true | ||
} | ||
}); |
Oops, something went wrong.