-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(sources): resolve globs and files using fp-ts
- Loading branch information
1 parent
a74b3c4
commit f165c1d
Showing
20 changed files
with
326 additions
and
96 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
This file was deleted.
Oops, something went wrong.
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,43 @@ | ||
import { isArrayOfStrings } from 'expect-more'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { flow, pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import { sync as globSync } from 'glob'; | ||
import { CWD } from '../../../constants'; | ||
import { getPatterns, Options } from './get-patterns'; | ||
import { removeReadonlyType } from './readonly'; | ||
import { tapNone } from './tap'; | ||
import { getErrorOrElse } from './try-catch'; | ||
|
||
type MaybeFilePaths = O.Option<string[]>; | ||
type EitherMaybeFilePaths = E.Either<Error, MaybeFilePaths>; | ||
|
||
/** | ||
* Using --source options and/or config files on disk from npm/pnpm/yarn/lerna, | ||
* return an array of absolute paths to every package.json file the user is | ||
* working with. | ||
* | ||
* @returns Array of absolute file paths to package.json files | ||
*/ | ||
export function getFilePaths(program: Options): EitherMaybeFilePaths { | ||
return pipe( | ||
getPatterns(program), | ||
E.traverseArray(resolvePattern), | ||
E.map(flow(removeReadonlyType, mergeArrayOfOptionsIntoOne, O.filter(isArrayOfStrings))), | ||
); | ||
|
||
function resolvePattern(pattern: string): EitherMaybeFilePaths { | ||
return pipe( | ||
E.tryCatch( | ||
() => globSync(pattern, { absolute: true, cwd: CWD }), | ||
getErrorOrElse(`npm package "glob" threw on pattern "${pattern}"`), | ||
), | ||
E.map(flow(O.of, O.filter(isArrayOfStrings), tapNone<string[]>(`found 0 files matching pattern "${pattern}"`))), | ||
); | ||
} | ||
|
||
function mergeArrayOfOptionsIntoOne(options: MaybeFilePaths[]): MaybeFilePaths { | ||
const unwrap = O.getOrElse<string[]>(() => []); | ||
return O.of(options.reduce<string[]>((values, option) => values.concat(unwrap(option)), [])); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/commands/lib/get-wrappers/get-patterns/get-lerna-patterns.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,20 @@ | ||
import { isArrayOfStrings } from 'expect-more'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { flow, pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import { join } from 'path'; | ||
import { MaybePatterns } from '.'; | ||
import { CWD } from '../../../../constants'; | ||
import { props } from './props'; | ||
import { readJsonSafe } from './read-json-safe'; | ||
|
||
export function getLernaPatterns(): MaybePatterns { | ||
return pipe( | ||
readJsonSafe(join(CWD, 'lerna.json')), | ||
E.map(flow(props('contents.packages'), O.filter(isArrayOfStrings))), | ||
E.match( | ||
(): MaybePatterns => O.none, | ||
(value) => value, | ||
), | ||
); | ||
} |
28 changes: 28 additions & 0 deletions
28
src/commands/lib/get-wrappers/get-patterns/get-pnpm-patterns.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,28 @@ | ||
import { isArrayOfStrings } from 'expect-more'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { flow, pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import { join } from 'path'; | ||
import type { MaybePatterns } from '.'; | ||
import { CWD } from '../../../../constants'; | ||
import { props } from './props'; | ||
import { readYamlSafe } from './read-yaml-safe'; | ||
|
||
interface PnpmWorkspace { | ||
packages?: string[]; | ||
} | ||
|
||
export function getPnpmPatterns(): MaybePatterns { | ||
return pipe( | ||
// packages: | ||
// - "packages/**" | ||
// - "components/**" | ||
// - "!**/test/**" | ||
readYamlSafe<PnpmWorkspace>(join(CWD, 'pnpm-workspace.yaml')), | ||
E.map(flow(props('packages'), O.filter(isArrayOfStrings))), | ||
E.match( | ||
(): MaybePatterns => O.none, | ||
(value) => value, | ||
), | ||
); | ||
} |
30 changes: 30 additions & 0 deletions
30
src/commands/lib/get-wrappers/get-patterns/get-yarn-patterns.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,30 @@ | ||
import { isArrayOfStrings } from 'expect-more'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import { join } from 'path'; | ||
import type { MaybePatterns } from '.'; | ||
import { Source } from '..'; | ||
import { CWD } from '../../../../constants'; | ||
import { props } from './props'; | ||
import { readJsonSafe } from './read-json-safe'; | ||
|
||
export function getYarnPatterns(): MaybePatterns { | ||
return pipe( | ||
readJsonSafe(join(CWD, 'package.json')), | ||
E.map((file) => pipe(findPackages(file.contents))), | ||
O.fromEither, | ||
O.flatten, | ||
); | ||
|
||
function findPackages(yarn: Source): MaybePatterns { | ||
return pipe( | ||
getArrayOfStrings('workspaces', yarn), | ||
O.fold(() => getArrayOfStrings('workspaces.packages', yarn), O.some), | ||
); | ||
} | ||
|
||
function getArrayOfStrings(paths: string, yarn: Source): MaybePatterns { | ||
return pipe(yarn, props(paths), O.filter(isArrayOfStrings)); | ||
} | ||
} |
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,50 @@ | ||
import { isArrayOfStrings } from 'expect-more'; | ||
import { flow, pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import { join } from 'path'; | ||
import { ALL_PATTERNS, CWD, SyncpackConfig } from '../../../../constants'; | ||
import { tapNone } from '../tap'; | ||
import { getLernaPatterns } from './get-lerna-patterns'; | ||
import { getPnpmPatterns } from './get-pnpm-patterns'; | ||
import { getYarnPatterns } from './get-yarn-patterns'; | ||
|
||
type Patterns = string[]; | ||
|
||
export type Options = Pick<SyncpackConfig, 'source'>; | ||
export type MaybePatterns = O.Option<Patterns>; | ||
|
||
/** | ||
* Find every glob pattern which should be used to find package.json files for | ||
* this monorepo. | ||
* | ||
* @returns `['./package.json', './packages/* /package.json']` | ||
*/ | ||
export function getPatterns(program: Options): Patterns { | ||
return pipe( | ||
O.of(program.source), | ||
O.filter(isArrayOfStrings), | ||
tapNone<Patterns>('no --source patterns found'), | ||
O.fold( | ||
flow( | ||
getYarnPatterns, | ||
tapNone<Patterns>('no yarn workspaces found'), | ||
O.fold(getPnpmPatterns, O.of), | ||
tapNone<Patterns>('no pnpm workspaces found'), | ||
O.fold(getLernaPatterns, O.of), | ||
tapNone<Patterns>('no lerna packages found'), | ||
O.map(flow(addRootDir, limitToPackageJson)), | ||
), | ||
O.of, | ||
), | ||
tapNone<Patterns>('no patterns found, using defaults'), | ||
O.getOrElse(() => ALL_PATTERNS), | ||
); | ||
|
||
function addRootDir(patterns: Patterns): Patterns { | ||
return [CWD, ...patterns]; | ||
} | ||
|
||
function limitToPackageJson(patterns: Patterns): Patterns { | ||
return patterns.map((pattern) => join(pattern, 'package.json')); | ||
} | ||
} |
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,19 @@ | ||
import { pipe } from 'fp-ts/lib/function'; | ||
import * as O from 'fp-ts/lib/Option'; | ||
import * as C from 'fp-ts/lib/ReadonlyRecord'; | ||
import * as S from 'fp-ts/lib/State'; | ||
|
||
/** | ||
* Safely read nested properties of any value. | ||
* @param keys 'child.grandChild.greatGrandChild' | ||
* @see https://gist.github.com/JamieMason/c0a3b21184cf8c43f76c77878c7c9198 | ||
*/ | ||
export function props(keys: string) { | ||
return function getNestedProp(obj: unknown): O.Option<unknown> { | ||
return pipe( | ||
keys.split('.'), | ||
S.traverseArray((key: string) => S.modify(O.chain(C.lookup(key) as never))), | ||
S.execute(O.fromNullable<unknown>(obj)), | ||
); | ||
}; | ||
} |
26 changes: 26 additions & 0 deletions
26
src/commands/lib/get-wrappers/get-patterns/read-json-safe.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,26 @@ | ||
import { parse } from 'fp-ts/Json'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { pipe } from 'fp-ts/lib/function'; | ||
import { readFileSync } from 'fs-extra'; | ||
import { SourceWrapper } from '..'; | ||
import { getErrorOrElse } from '../try-catch'; | ||
|
||
export function readJsonSafe(filePath: string): E.Either<Error | SyntaxError, SourceWrapper> { | ||
return pipe( | ||
readFileSafe(filePath), | ||
E.chain((json) => | ||
pipe( | ||
parse(json), | ||
E.mapLeft(getErrorOrElse(`Failed to parse JSON file at ${filePath}`)), | ||
E.map((contents) => ({ contents, filePath, json } as SourceWrapper)), | ||
), | ||
), | ||
); | ||
} | ||
|
||
function readFileSafe(filePath: string): E.Either<Error, string> { | ||
return E.tryCatch( | ||
() => readFileSync(filePath, { encoding: 'utf8' }), | ||
getErrorOrElse(`Failed to read JSON file at ${filePath}`), | ||
); | ||
} |
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 * as E from 'fp-ts/lib/Either'; | ||
import { sync as readYamlSync } from 'read-yaml-file'; | ||
import { getErrorOrElse } from '../try-catch'; | ||
|
||
export function readYamlSafe<T = unknown>(filePath: string): E.Either<Error, T> { | ||
return E.tryCatch(() => readYamlSync<T>(filePath), getErrorOrElse(`Failed to read YAML file at ${filePath}`)); | ||
} |
Oops, something went wrong.