UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

179 lines (178 loc) • 9.07 kB
import ignore = require('ignore'); import { Tree } from '../generators/tree'; export declare function getIgnoreObject(root?: string): ReturnType<typeof ignore>; /** One directory's ignore files, and the directory its patterns are rooted at. */ export type ScopedIgnoreMatcher = { /** Workspace-relative POSIX directory, `''` for the workspace root. */ dir: string; /** How they relate is decided at build time - see `merge`. */ matchers: ReturnType<typeof ignore>[]; }; /** * Resolves the ignore files that apply to a directory: its own and every one * above it, up to the workspace root. * * Ignore files cascade - a `.gitignore` covers its own directory and below, and * its patterns are relative to *itself*, not to the workspace root. Reading only * the root file, which is what `getIgnoreObject` does, silently misses every * nested one. * * A directory's answer is its own files plus its parent's, so every directory on * the way up is memoized rather than only the one asked for: sibling leaves * share the whole trunk, and a later walk stops at the first directory already * known. * * `read` decides where the files come from - `tree.read` for a generator, disk * for a caller with no tree - and returns an empty string or null when there is * no such file. Paths handed to it are workspace-relative POSIX. * * `merge` decides how the files *within one directory* relate, and the two * consumers genuinely need different rules: * * - `false` is prettier's: one matcher per file, any of them excluding wins, and * a negation counts only if none excluded. `createIsIgnoredFunction` builds an * ignorer per `--ignore-path` and ORs them, so a `!x` in `.prettierignore` * cannot re-include an `x` that `.gitignore` excluded. * - `true` is git's and the native walker's: all files in one matcher, so * `.nxignore`'s `!x` removes `.gitignore`'s exclusion of `x` outright. It has * to be a merge rather than a precedence check between separate matchers, * because a lone `!x` in its own matcher reports an opinion on `x/` but *none* * on `x/a.ts` (measured), so the exclusion would still reach the children. * The merge only removes the exclusion within that one directory - a negation * in a nested file still loses to an ancestor's exclusion. * * When `merge` is true, `filenames` order matters: they go into one matcher in * order and the last matching pattern decides, so list them lowest-authority * first. */ export declare function createIgnoreChainResolver(read: (path: string) => string | null | undefined, filenames: string[], merge: boolean): (dir: string) => ScopedIgnoreMatcher[]; /** * True when the file is ignored, resolving the chain nearest file first. * * Each matcher is tested against the path relative to its own directory, which * is what makes a nested pattern like `/build` mean that directory's `build` * rather than the workspace's. * * Nearest directory with an *opinion* wins, not the first match: a nested * `!keep.log` must override the root's `*.log`, which is git's rule for files. * A nested negation of a *directory* does not reach its children - see the * `merge` note on `createIgnoreChainResolver`. * * How the files of one directory relate is decided when the chain is built - see * that same note. Here they are simply the entry's matchers: any one excluding * wins, and a negation counts only if none excluded. * * Two preconditions, neither enforced here: * * - `filePath` is workspace-relative POSIX and must sit under every `dir` in the * chain, which holds when the chain came from that file's own directory. * - No ancestor directory of `filePath` may itself be ignored. git refuses to * re-include a file inside an excluded directory, and this does not implement * that rule: asked directly about `dist/keep.ts` with a root `dist/` and a * nested `dist/.gitignore` holding `!keep.ts`, it answers "not ignored" where * git says ignored (measured). A pruning walk like `visitNotIgnoredFiles` * satisfies it by never asking about anything under `dist/`; a per-file * caller cannot, so it goes through `createAncestorAwareIgnoreChecker`, * which checks the ancestors before asking the chain. */ export declare function isIgnoredByChain(chain: ScopedIgnoreMatcher[], filePath: string): boolean; /** * The chain's answers plus git's excluded-ancestor rule: nothing inside an * ignored directory can be re-included, so a nested negation must not * resurrect a file whose ancestor an outer file excluded - the case * `isIgnoredByChain` alone gets wrong (see its second precondition). The * oxfmt CLI follows the same rule (measured against 0.60.0: a scan skips a * nested `!keep.ts` under a root-ignored `dist/`), so every per-file caller - * the cascading tree checkers and the disk-backed resolver in * `formatters/oxfmt.ts` - goes through here. * * A directory's own ignore files cannot un-ignore the directory itself, so * its verdict comes from its parent's chain - probed with a trailing slash, * which is what makes a directory-only pattern like `dist/` match. Verdicts * are memoized per directory, so a batch of files shares its ancestor walks. */ export declare function createAncestorAwareIgnoreChecker(resolve: (dir: string) => ScopedIgnoreMatcher[]): TreeIgnoreChecker; /** * Directories that should never be walked, whatever the workspace's own ignore * files say - `node_modules`, `.git`, the nx caches. * * The list comes from the native walker rather than a second copy here, so a * filesystem walk and a tree walk cannot drift apart. * * Checked ahead of the cascading chain rather than folded into it: these are not * re-includable, and as ordinary patterns a nested negation could resurrect * `node_modules`. */ export declare function isAlwaysIgnored(path: string): boolean; /** * A chain's answers as predicates over workspace-relative POSIX paths. * * Files and directories are asked separately because the answers differ: a * pattern is only directory-only if it ends in a slash, and `ignore` will not * match `dist/` against the path `dist`. Callers must not have to know that, so * the slash is appended inside and never leaves this module. */ export type TreeIgnoreChecker = { isIgnoredFile: (path: string) => boolean; isIgnoredDirectory: (path: string) => boolean; }; /** * What git ignores, which is also what the native walker ignores. * * `.nxignore` outranks `.gitignore` - `walker.rs` registers it with * `add_custom_ignore_filename` - which a merge with `.nxignore` last reproduces. * git itself does not read it. * * Reads from the tree rather than disk because a generator can create or amend * an ignore file in the same run, which would leave the on-disk copy stale. */ export declare function createGitIgnoreChecker(tree: Tree): TreeIgnoreChecker; /** * What prettier ignores: the workspace root only, and one ignorer per * `--ignore-path` ORed rather than merged (both measured), so a `!` in * `.prettierignore` cannot re-include what `.gitignore` excluded. That is the * CLI `nx format:check` shells out to. * * Not an exact match for that command: `isAlwaysIgnored` is wider than * prettier's built-ins, and `format.ts` filters its own patterns through * `.nxignore`, which this does not read. * * Reads from the tree rather than disk, as above. */ export declare function createPrettierIgnoreChecker(tree: Tree): TreeIgnoreChecker; /** * What oxfmt ignores: prettier's two files, but resolved from each file's own * directory upwards rather than the workspace root - measured against the * oxfmt 0.60.0 CLI, which differs from prettier on exactly that axis. Still * one matcher per file rather than merged. * * A config's `ignorePatterns` is not an ignore file and is not read here; * `formatFilesWithOxfmt` applies it rooted at that config's directory. */ export declare function createOxfmtIgnoreChecker(tree: Tree): TreeIgnoreChecker; /** * Exported, unlike git's and prettier's, because oxfmt has two consumers: * this tree-backed checker and the disk-backed resolver in * `formatters/oxfmt.ts`. A shared value is the only thing that keeps them * agreeing, so do not restate these three anywhere. * * `satisfies` rather than an annotation keeps the values literal - an * annotation widens `cascade` and `merge` to `boolean` (measured in the * declaration emit). */ export declare const OXFMT_IGNORE_OPTIONS: { filenames: string[]; cascade: true; merge: false; }; /** * `path.dirname` for the workspace-relative POSIX paths the chain is keyed by, * except that the workspace root is `''` rather than `.` - that is the key * `createIgnoreChainResolver` terminates on. */ export declare function posixDirname(relativePath: string): string; /** * Adds an entry to a .gitignore file if it's not already covered by existing patterns. * Creates the file if it doesn't exist. */ export declare function addEntryToGitIgnore(tree: Tree, gitignorePath: string, entry: string): void;