@williamthorsen/nmr 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,23 +3,17 @@
3
3
  Context-aware script runner for pnpm monorepos. Ships an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
4
4
 
5
5
  <!-- section:release-notes -->
6
- ## Release notes — v0.21.0 (2026-07-30)
6
+ ## Release notes — v0.22.0 (2026-07-31)
7
7
 
8
8
  ### 🎉 Features
9
9
 
10
- - Add a shared Vitest config export (#526)
10
+ - 🚨 **Breaking:** Ship shell and Dockerfile formatting from a shared nmr Prettier config (#540)
11
11
 
12
- Adds a shared Vitest configuration that can be used by repos that use `nmr`. Tests are now sorted into three named categories (unit, integration, and app), so that a run can target or skip a single category. A second variant excludes all tests in workspace packages, allowing the monorepo's own root-level tests to be run separately.
12
+ Adds the ability to format shell scripts and Dockerfiles to `nmr fmt`. `@williamthorsen/nmr` now provides a common config for consistency across projects. Shell fences inside Markdown are now formatted as well. These features are absorbed into `nmr fmt` and `nmr fmt:check`; the `fmt:sh` and `fmt:all` commands are removed.
13
13
 
14
- - 🚨 **Breaking:** Retire the Vitest config-file variants from nmr's defaults (#533)
14
+ - 🚨 **Breaking:** Require the monorepo root in the shared root Vitest config (#541)
15
15
 
16
- Packages no longer need extra Vitest config files to keep integration tests out of the default test run. Integration tests are now recognized by an `*.int.test.ts` suffix, and every package gets the same five commands: `test`, `test:all`, `test:coverage`, `test:integration`, and `test:watch`. The same five commands work from the monorepo root. A Vitest run that collects no test files now passes instead of failing. Consumers should migrate the Vitest config when upgrading.
17
-
18
- ### 🐛 Bug fixes
19
-
20
- - Apply ignore rules from any working directory when formatting (#535)
21
-
22
- Fixes an issue where `nmr fmt` and `nmr fmt:check` did not apply a pattern from a package's `.gitignore` or `.prettierignore` when the run started at the monorepo root, so files a package excluded could fail a root-level format gate while passing a package-scoped one. Untracked files that git does not ignore are now formatted alongside tracked ones, and a run started outside a git repository now fails instead of reporting a clean pass over nothing. Trailing arguments to `nmr fmt` are git pathspecs rather than Prettier flags; one that matches no formattable file now fails instead of exiting silently.
16
+ Modifies the testing configuration so that root-level test runs always cover the repository containing the configuration. The shared root Vitest config now requires a `monorepoRoot` option specifying an absolute path to the repository's own root, in place of the optional search origin. A repository nested inside a parent monorepo fails instead of resolving silently to its parent.
23
17
  <!-- /section:release-notes -->
24
18
 
25
19
  ## Installation
@@ -46,17 +40,17 @@ nmr works out of the box with no configuration. It ships with built-in scripts f
46
40
  From a package directory:
47
41
 
48
42
  ```bash
49
- nmr test # Run tests for the current package
50
- nmr build # Compile to .js and .d.ts in one pass
51
- nmr check # Typecheck, format check, lint check, and tests
43
+ nmr test # Run tests for the current package
44
+ nmr build # Compile to .js and .d.ts in one pass
45
+ nmr check # Typecheck, format check, lint check, and tests
52
46
  ```
53
47
 
54
48
  From the monorepo root:
55
49
 
56
50
  ```bash
57
- nmr test # Run root tests + recursive workspace tests
58
- nmr build # Build all packages
59
- nmr ci # Run build && check:strict && audit (full CI pipeline)
51
+ nmr test # Run root tests + recursive workspace tests
52
+ nmr build # Build all packages
53
+ nmr ci # Run build && check:strict && audit (full CI pipeline)
60
54
  ```
61
55
 
62
56
  nmr detects where you are and selects the right scripts automatically — see [context-aware resolution](#context-aware-resolution) below.
@@ -217,9 +211,9 @@ The second example calls the bin directly, which sidesteps the workspace-versus-
217
211
  The upgrade tool ([taze](https://github.com/antfu-collective/taze)) arrives with nmr, so your repo declares no dependency on it. Everything after the command name is passed through, including the range mode:
218
212
 
219
213
  ```bash
220
- nmr upgrade # upgrades available within each package's version ceilings
221
- nmr upgrade major # major upgrades, still inside the ceilings
222
- nmr upgrade --write # apply the proposals to package.json
214
+ nmr upgrade # upgrades available within each package's version ceilings
215
+ nmr upgrade major # major upgrades, still inside the ceilings
216
+ nmr upgrade --write # apply the proposals to package.json
223
217
  ```
224
218
 
225
219
  ### Configuring upgrades
@@ -348,12 +342,10 @@ The same five names the workspace registry carries, so a command means the same
348
342
 
349
343
  #### Format
350
344
 
351
- | Command | Runs |
352
- | ----------- | ----------------------- |
353
- | `fmt` | `nmr-fmt --write` |
354
- | `fmt:all` | `fmt`, `fmt:sh` |
355
- | `fmt:check` | `nmr-fmt --check` |
356
- | `fmt:sh` | `shfmt --write **/*.sh` |
345
+ | Command | Runs |
346
+ | ----------- | ----------------- |
347
+ | `fmt` | `nmr-fmt --write` |
348
+ | `fmt:check` | `nmr-fmt --check` |
357
349
 
358
350
  #### File selection
359
351
 
@@ -423,19 +415,19 @@ Position determines ownership: flags before the command name are nmr's own, and
423
415
 
424
416
  ```bash
425
417
  # From a package directory
426
- nmr test # Run workspace test script
427
- nmr build # Compile to .js and .d.ts in one pass
418
+ nmr test # Run workspace test script
419
+ nmr build # Compile to .js and .d.ts in one pass
428
420
 
429
421
  # From the monorepo root
430
- nmr test # Root tests + recursive workspace tests
431
- nmr ci # build + check:strict + audit
422
+ nmr test # Root tests + recursive workspace tests
423
+ nmr ci # build + check:strict + audit
432
424
 
433
425
  # Target specific packages
434
- nmr --filter core test # Test only the core package
435
- nmr --recursive lint # Lint all workspace packages
426
+ nmr --filter core test # Test only the core package
427
+ nmr --recursive lint # Lint all workspace packages
436
428
 
437
429
  # Force root context from anywhere
438
- nmr --workspace-root check # Run root check from a package dir
430
+ nmr --workspace-root check # Run root check from a package dir
439
431
  ```
440
432
 
441
433
  ## Additional subcommands
@@ -455,8 +447,8 @@ nmr report-overrides
455
447
  Sync the agent-facing guidance shipped with nmr into the consuming repo.
456
448
 
457
449
  ```bash
458
- nmr sync-agent-files # write .agents/nmr/AGENTS.md, stamped with the installed nmr version
459
- nmr sync-agent-files --check # verify the stamp matches; exit 1 with a fix message if not
450
+ nmr sync-agent-files # write .agents/nmr/AGENTS.md, stamped with the installed nmr version
451
+ nmr sync-agent-files --check # verify the stamp matches; exit 1 with a fix message if not
460
452
  ```
461
453
 
462
454
  Run `nmr sync-agent-files` once after upgrading nmr. The generated file is committed to the consuming repo; do not edit it by hand.
@@ -570,12 +562,65 @@ for (const packageDir of getWorkspacePackageDirs(monorepoRoot)) {
570
562
 
571
563
  `findMonorepoRoot(startDir?)` walks up from `startDir`, defaulting to `process.cwd()`, until it reaches a directory containing `pnpm-workspace.yaml`. It throws if it runs out of parent directories without finding one.
572
564
 
573
- `getWorkspacePackageDirs(monorepoRoot)` reads the workspace patterns from that repo's `pnpm-workspace.yaml` and resolves them to absolute package directories, sorted and free of duplicates. Patterns carry pnpm's own semantics: `packages/*`, deeper globs such as `packages/**`, exact paths such as `tools/cli`, and `!`-prefixed exclusions such as `!packages/legacy` or `!**/test/**`, which filter every directory the positive patterns matched regardless of where they appear in the list. Nothing under `node_modules` is ever returned.
565
+ `getWorkspacePackageDirs(monorepoRoot)` reads the workspace patterns from that repo's `pnpm-workspace.yaml` and resolves them to absolute package directories, sorted and free of duplicates. It throws if `monorepoRoot` holds no `pnpm-workspace.yaml`. Patterns carry pnpm's own semantics: `packages/*`, deeper globs such as `packages/**`, exact paths such as `tools/cli`, and `!`-prefixed exclusions such as `!packages/legacy` or `!**/test/**`, which filter every directory the positive patterns matched regardless of where they appear in the list. Nothing under `node_modules` is ever returned.
574
566
 
575
567
  One divergence from pnpm: a directory counts as a package only if it holds a `package.json`, not a `package.yaml` or `package.json5`.
576
568
 
577
569
  Quote exclusion patterns in the manifest — `- '!packages/legacy'`. An unquoted `!` opens a YAML tag rather than a string, so the entry never reaches nmr (or pnpm) as a pattern.
578
570
 
571
+ ## Shared Prettier config
572
+
573
+ Every repo consuming nmr otherwise maintains its own copy of the house Prettier options, and wires up shell formatting itself. The `@williamthorsen/nmr/prettier` subpath publishes both as a factory, so a repo declares only what it customizes:
574
+
575
+ ```js
576
+ // .prettierrc.js
577
+ import { definePrettierConfig } from '@williamthorsen/nmr/prettier';
578
+
579
+ export default definePrettierConfig();
580
+ ```
581
+
582
+ `prettier` is a peer dependency (`>=3.9.5 <4`), declared optional — the consuming repo provides it. The shell and Dockerfile plugin is a dependency of nmr rather than a peer, so its version is pinned centrally and two repos cannot format the same script differently.
583
+
584
+ ### What it formats
585
+
586
+ Beyond everything Prettier already handles, this config formats `.sh`, `.bash`, `.zsh`, and the other extensions and dotfile names of the Shell language, plus `Dockerfile` and `Containerfile`. `nmr fmt` therefore covers shell scripts with no extra script, glob, or workflow entry.
587
+
588
+ Shell output matches `shfmt` run with no flags: `binaryNextLine`, `spaceRedirects`, and `switchCaseIndent` are pinned to shfmt's CLI defaults, which the underlying plugin inverts. A repo whose scripts shfmt already formatted sees no diff on adoption.
589
+
590
+ ### Language scoping
591
+
592
+ `prettier-plugin-sh` routes 20 further file types to the same shell parser, among them `.gitignore`, `.env`, `.csh`, `.nu`, `.properties`, `.ics`, `.vcf`, `CODEOWNERS`, and `hosts`. Since `nmr fmt` hands git's whole file list to Prettier, registering the plugin as shipped would make every one of them formattable — and the shell parser either fails on them or silently rewrites them. A `.gitignore` pattern such as `a(b)c` fails to parse, and an unquoted `&` in a `.env` or `.properties` value is split across two lines.
593
+
594
+ This config registers a narrowed plugin, so Prettier infers a parser for the Shell and Dockerfile languages alone. `.flaskenv`, `gradlew`, and `mvnw` are dropped from Shell as well: the first holds dotenv content the plugin misfiles as shell, and the other two are vendored wrappers their generators would overwrite.
595
+
596
+ The parsers themselves stay registered, so a repo that wants a dropped language back assigns it explicitly:
597
+
598
+ ```js
599
+ export default definePrettierConfig({
600
+ additionalOverrides: [{ files: ['APKBUILD', '*.ebuild'], options: { parser: 'sh' } }],
601
+ });
602
+ ```
603
+
604
+ ### Customizing
605
+
606
+ Any Prettier option passed to the factory spreads over the defaults:
607
+
608
+ ```js
609
+ export default definePrettierConfig({
610
+ printWidth: 100,
611
+ additionalPlugins: [await import('prettier-plugin-tailwindcss')],
612
+ additionalOverrides: [{ files: ['*.md'], options: { proseWrap: 'always' } }],
613
+ });
614
+ ```
615
+
616
+ `additionalPlugins` and `additionalOverrides` append to what the config declares, so a later override wins over nmr's own and no seam can drop shell support. Passing `plugins` or `overrides` throws, and both are typed `never` — replacement is inexpressible rather than merely discouraged, because a silently ignored key would produce no diff to notice.
617
+
618
+ ### Adoption caveats
619
+
620
+ - **Indentation follows `.editorconfig`**, as it did under shfmt: Prettier maps `indent_style` and `indent_size` to `useTabs` and `tabWidth`, and the plugin honours both. With no `.editorconfig`, the two disagree — shfmt indents shell with tabs, Prettier with two spaces. Add `indent_style = tab` under `[*.sh]` to keep tabs.
621
+ - **Shell fences in Markdown are formatted**, which collapses hand-aligned comment columns in documentation. Prettier keeps the original text when an embedded snippet fails to parse, so illustrative or truncated fences are safe. Use `<!-- prettier-ignore -->` before a fence whose alignment matters.
622
+ - **Adopting the house options reformats the repo.** `singleQuote`, `trailingComma`, and the rest apply on the first run; review that diff separately from the shell one.
623
+
579
624
  ## Shared Vitest config
580
625
 
581
626
  Every repo consuming nmr otherwise writes and maintains its own Vitest config. The `@williamthorsen/nmr/vitest` subpath publishes that config as a factory, so a repo declares only what it customizes:
@@ -602,9 +647,9 @@ The config declares three projects, named for what the tests cover:
602
647
  All three match only under a `__tests__` directory. Select them at run time with `--project`, which accepts negation:
603
648
 
604
649
  ```bash
605
- vitest --project '!integration' # everything except integration tests
606
- vitest --project integration # integration tests alone
607
- vitest # every project
650
+ vitest --project '!integration' # everything except integration tests
651
+ vitest --project integration # integration tests alone
652
+ vitest # every project
608
653
  ```
609
654
 
610
655
  `unit` is defined by subtracting the other categories rather than by an allow-list of suffixes, so a file such as `parser.smoke.test.ts` runs under `unit` instead of being silently dropped.
@@ -634,22 +679,18 @@ A monorepo's own root-level tests need a second config, because the package conf
634
679
  // vitest.root.config.ts
635
680
  import { defineRootVitestConfig } from '@williamthorsen/nmr/vitest';
636
681
 
637
- export default defineRootVitestConfig();
682
+ export default defineRootVitestConfig({ monorepoRoot: import.meta.dirname });
638
683
  ```
639
684
 
640
685
  This variant reads `pnpm-workspace.yaml` and excludes every workspace package from all three projects, so a root run covers only root-level files. It reports no coverage of its own — packages cover their own sources.
641
686
 
642
- The monorepo root is located by walking up from the working directory, and every project is pinned to it, so the exclusions hold wherever the run is invoked from. Pass `startDir` to locate it from somewhere else:
643
-
644
- ```ts
645
- export default defineRootVitestConfig({ startDir: import.meta.dirname });
646
- ```
687
+ `monorepoRoot` is required, and because the config sits at the monorepo root, it is always `import.meta.dirname`. Stating the root rather than searching for it is what makes the exclusions describe this repo: a search from the working directory would resolve whichever monorepo the run started in, and every project is then pinned to that one. A directory holding no `pnpm-workspace.yaml` throws, naming the directory.
647
688
 
648
689
  ### Migrating from the config-file variants
649
690
 
650
691
  nmr once selected a package's test scripts by looking for a `vitest.integration.config.ts` on disk, which meant three config files per package that separated its integration tests. Those files are no longer consulted. **Migrate the config and the scripts together**: one without the other leaves the repo in one of the two states below.
651
692
 
652
- 1. Replace the repo's root `vitest.config.ts` with `defineVitestConfig()`, and its root-scoped config with `defineRootVitestConfig()`.
693
+ 1. Replace the repo's root `vitest.config.ts` with `defineVitestConfig()`, and its root-scoped config with `defineRootVitestConfig({ monorepoRoot: import.meta.dirname })`.
653
694
  2. Delete every `vitest.integration.config.ts` and `vitest.standalone.config.ts`, plus any per-package `vitest.config.ts` that only re-exports an ancestor. Vitest resolves config by walking up from the run root, so those are redundant.
654
695
  3. Rename integration tests to `*.int.test.ts` and tooling or drift tests to `*.app.test.ts`.
655
696
  4. Drop any hand-copied `test*` entries from `package.json`, which now shadow the defaults.
@@ -33,9 +33,7 @@ export const rootScripts = {
33
33
  fix: ['lint', 'fmt'],
34
34
  'fix:check': ['fmt:check', 'lint:check'],
35
35
  fmt: 'nmr-fmt --write',
36
- 'fmt:all': ['fmt', 'fmt:sh'],
37
36
  'fmt:check': 'nmr-fmt --check',
38
- 'fmt:sh': 'shfmt --write **/*.sh',
39
37
  lint: 'nmr root:lint && pnpm --recursive exec nmr lint',
40
38
  'lint:check': 'nmr root:lint:check && pnpm --recursive exec nmr lint:check',
41
39
  'lint:strict': 'nmr root:lint:strict && pnpm --recursive exec nmr lint:strict',
@@ -0,0 +1,8 @@
1
+ import type { Config } from 'prettier';
2
+ export interface PrettierConfigOptions extends Omit<Config, 'overrides' | 'plugins'> {
3
+ additionalPlugins?: NonNullable<Config['plugins']>;
4
+ additionalOverrides?: NonNullable<Config['overrides']>;
5
+ overrides?: never;
6
+ plugins?: never;
7
+ }
8
+ export declare function definePrettierConfig(options?: PrettierConfigOptions): Config;
@@ -0,0 +1,60 @@
1
+ import * as shPlugin from 'prettier-plugin-sh';
2
+ const INFERRED_LANGUAGES = new Set(['Dockerfile', 'Shell']);
3
+ const EXCLUDED_FILENAMES = new Set([
4
+ '.bash_history',
5
+ '.cshrc',
6
+ '.flaskenv',
7
+ '.login',
8
+ 'cshrc',
9
+ 'gradlew',
10
+ 'login',
11
+ 'mvnw',
12
+ ]);
13
+ const DEFAULT_OPTIONS = {
14
+ binaryNextLine: false,
15
+ checkIgnorePragma: true,
16
+ jsxSingleQuote: false,
17
+ singleQuote: true,
18
+ spaceRedirects: false,
19
+ switchCaseIndent: false,
20
+ trailingComma: 'all',
21
+ };
22
+ const DEFAULT_OVERRIDES = [
23
+ {
24
+ files: ['*.json5', '*.jsonc', 'tsconfig.json', 'tsconfig.*.json'],
25
+ options: { parser: 'jsonc', singleQuote: false, trailingComma: 'all' },
26
+ },
27
+ ];
28
+ export function definePrettierConfig(options = {}) {
29
+ assertNoReplacement(options);
30
+ const { additionalOverrides = [], additionalPlugins = [], ...overrideOptions } = options;
31
+ return {
32
+ ...DEFAULT_OPTIONS,
33
+ ...overrideOptions,
34
+ overrides: [...DEFAULT_OVERRIDES, ...additionalOverrides],
35
+ plugins: [buildShellPlugin(), ...additionalPlugins],
36
+ };
37
+ }
38
+ function buildShellPlugin() {
39
+ return {
40
+ ...shPlugin,
41
+ languages: shPlugin.languages.filter(isInferredLanguage).map(withoutExcludedFilenames),
42
+ };
43
+ }
44
+ function isInferredLanguage(language) {
45
+ return INFERRED_LANGUAGES.has(language.name);
46
+ }
47
+ function withoutExcludedFilenames(language) {
48
+ if (language.filenames === undefined)
49
+ return language;
50
+ return { ...language, filenames: language.filenames.filter((name) => !EXCLUDED_FILENAMES.has(name)) };
51
+ }
52
+ function assertNoReplacement(options) {
53
+ if ('overrides' in options)
54
+ throw ownedKeyError('overrides', 'additionalOverrides');
55
+ if ('plugins' in options)
56
+ throw ownedKeyError('plugins', 'additionalPlugins');
57
+ }
58
+ function ownedKeyError(key, seam) {
59
+ return new TypeError(`definePrettierConfig: \`${key}\` is owned by this config. Use \`${seam}\` to append to it.`);
60
+ }
@@ -9,7 +9,7 @@ export interface VitestConfigOptions {
9
9
  project?: ProjectConfig;
10
10
  }
11
11
  export interface RootVitestConfigOptions extends VitestConfigOptions {
12
- startDir?: string;
12
+ monorepoRoot: string;
13
13
  }
14
14
  export declare function defineVitestConfig(options?: VitestConfigOptions): ViteUserConfig;
15
- export declare function defineRootVitestConfig(options?: RootVitestConfigOptions): ViteUserConfig;
15
+ export declare function defineRootVitestConfig(options: RootVitestConfigOptions | undefined): ViteUserConfig;
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { defaultExclude, mergeConfig } from 'vitest/config';
3
- import { findMonorepoRoot, getWorkspacePackageDirs } from "./workspace.js";
3
+ import { getWorkspacePackageDirs } from "./workspace.js";
4
4
  const APP_PATTERNS = ['**/__tests__/**/*.app.test.{ts,tsx}'];
5
5
  const INTEGRATION_PATTERNS = ['**/__tests__/**/*.int.test.{ts,tsx}'];
6
6
  const ALL_TEST_PATTERNS = ['**/__tests__/**/*.test.{ts,tsx}'];
@@ -12,11 +12,18 @@ const COVERAGE_EXCLUDE = [
12
12
  '**/*.types.ts',
13
13
  ];
14
14
  const PACKAGE_COVERAGE_INCLUDE = ['**/src/**/*.{ts,tsx}'];
15
+ const MISSING_MONOREPO_ROOT = 'defineRootVitestConfig requires `monorepoRoot`, an absolute path to the directory holding pnpm-workspace.yaml. Pass `import.meta.dirname` from the root config.';
15
16
  export function defineVitestConfig(options = {}) {
16
17
  return buildConfig(options, { coverageInclude: PACKAGE_COVERAGE_INCLUDE });
17
18
  }
18
- export function defineRootVitestConfig(options = {}) {
19
- const monorepoRoot = findMonorepoRoot(options.startDir);
19
+ export function defineRootVitestConfig(options) {
20
+ if (options === undefined) {
21
+ throw new TypeError(MISSING_MONOREPO_ROOT);
22
+ }
23
+ const monorepoRoot = options.monorepoRoot;
24
+ if (typeof monorepoRoot !== 'string' || !path.isAbsolute(monorepoRoot)) {
25
+ throw new TypeError(MISSING_MONOREPO_ROOT);
26
+ }
20
27
  return buildConfig(options, {
21
28
  coverageInclude: [],
22
29
  projectExclude: getWorkspaceExcludePatterns(monorepoRoot),
@@ -3,21 +3,25 @@ import path from 'node:path';
3
3
  import { parse } from 'yaml';
4
4
  import { isObject } from "./helpers/type-guards.js";
5
5
  import { resolvePackageDirs } from "./helpers/workspace-patterns.js";
6
+ const WORKSPACE_MANIFEST = 'pnpm-workspace.yaml';
6
7
  export function findMonorepoRoot(startDir) {
7
8
  let dir = path.resolve(startDir ?? process.cwd());
8
9
  for (;;) {
9
- if (existsSync(path.join(dir, 'pnpm-workspace.yaml'))) {
10
+ if (existsSync(path.join(dir, WORKSPACE_MANIFEST))) {
10
11
  return dir;
11
12
  }
12
13
  const parent = path.dirname(dir);
13
14
  if (parent === dir) {
14
- throw new Error('Could not find monorepo root: no pnpm-workspace.yaml found in any parent directory');
15
+ throw new Error(`Could not find monorepo root: no ${WORKSPACE_MANIFEST} found in any parent directory`);
15
16
  }
16
17
  dir = parent;
17
18
  }
18
19
  }
19
20
  export function getWorkspacePackageDirs(monorepoRoot) {
20
- const workspaceFile = path.join(monorepoRoot, 'pnpm-workspace.yaml');
21
+ const workspaceFile = path.join(monorepoRoot, WORKSPACE_MANIFEST);
22
+ if (!existsSync(workspaceFile)) {
23
+ throw new Error(`Not a monorepo root: no ${WORKSPACE_MANIFEST} in ${monorepoRoot}`);
24
+ }
21
25
  const content = readFileSync(workspaceFile, 'utf8');
22
26
  const parsed = parse(content);
23
27
  const packages = getPackagesFromParsedYaml(parsed);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/nmr",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "private": false,
5
5
  "description": "Context-aware script runner for pnpm monorepos",
6
6
  "keywords": [
@@ -26,6 +26,10 @@
26
26
  "types": "./dist/esm/index.d.ts",
27
27
  "default": "./dist/esm/index.js"
28
28
  },
29
+ "./prettier": {
30
+ "types": "./dist/esm/prettier.d.ts",
31
+ "default": "./dist/esm/prettier.js"
32
+ },
29
33
  "./scripts": {
30
34
  "types": "./dist/esm/scripts.d.ts",
31
35
  "default": "./dist/esm/scripts.js"
@@ -61,6 +65,7 @@
61
65
  "dependencies": {
62
66
  "glob": "13.0.6",
63
67
  "jiti": "2.7.0",
68
+ "prettier-plugin-sh": "0.19.0",
64
69
  "taze": "19.16.0",
65
70
  "yaml": "2.9.0",
66
71
  "@williamthorsen/nmr-core": "0.7.3"