@sanity/pkg-utils 6.4.1 → 6.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/pkg-utils",
3
- "version": "6.4.1",
3
+ "version": "6.5.0",
4
4
  "description": "Simple utilities for modern npm packages.",
5
5
  "keywords": [
6
6
  "sanity-io",
@@ -88,7 +88,7 @@
88
88
  "parse-git-config": "^3.0.0",
89
89
  "pkg-up": "^3.1.0",
90
90
  "prettier": "^3.2.5",
91
- "prettier-plugin-packagejson": "^2.4.14",
91
+ "prettier-plugin-packagejson": "^2.5.0",
92
92
  "pretty-bytes": "^5.6.0",
93
93
  "prompts": "^2.4.2",
94
94
  "rimraf": "^4.4.1",
@@ -97,7 +97,8 @@
97
97
  "rxjs": "^7.8.1",
98
98
  "treeify": "^1.1.0",
99
99
  "uuid": "^9.0.1",
100
- "zod": "^3.22.4"
100
+ "zod": "3.22.4",
101
+ "zod-validation-error": "3.1.0"
101
102
  },
102
103
  "devDependencies": {
103
104
  "@commitlint/cli": "^19.2.1",
@@ -133,9 +134,9 @@
133
134
  "semver": "^7.6.0",
134
135
  "ts-node": "^10.9.2",
135
136
  "typescript": "5.4.5",
136
- "vitest": "^1.4.0",
137
+ "vitest": "^1.5.0",
137
138
  "vitest-github-actions-reporter": "^0.11.1",
138
- "@sanity/pkg-utils": "6.4.1"
139
+ "@sanity/pkg-utils": "6.5.0"
139
140
  },
140
141
  "peerDependencies": {
141
142
  "typescript": "5.4.x"
@@ -0,0 +1,145 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`{'noPackageJsonTypings': 'error'}} 1`] = `
4
+ {
5
+ "noImplicitBrowsersList": "warn",
6
+ "noImplicitSideEffects": "warn",
7
+ "noPackageJsonTypings": "error",
8
+ }
9
+ `;
10
+
11
+ exports[`{'noPackageJsonTypings': 'off'}} 1`] = `
12
+ {
13
+ "noImplicitBrowsersList": "warn",
14
+ "noImplicitSideEffects": "warn",
15
+ "noPackageJsonTypings": "off",
16
+ }
17
+ `;
18
+
19
+ exports[`{'noPackageJsonTypings': 'warn'}} 1`] = `
20
+ {
21
+ "noImplicitBrowsersList": "warn",
22
+ "noImplicitSideEffects": "warn",
23
+ "noPackageJsonTypings": "warn",
24
+ }
25
+ `;
26
+
27
+ exports[`{'noPackageJsonTypings': false}} 1`] = `
28
+ [ZodError: [
29
+ {
30
+ "code": "invalid_union",
31
+ "unionErrors": [
32
+ {
33
+ "issues": [
34
+ {
35
+ "received": false,
36
+ "code": "invalid_literal",
37
+ "expected": "error",
38
+ "path": [
39
+ "strictOptions",
40
+ "noPackageJsonTypings"
41
+ ],
42
+ "message": "Validation error: Invalid literal value, expected \\"error\\" at \\"strictOptions.noPackageJsonTypings\\""
43
+ }
44
+ ],
45
+ "name": "ZodError"
46
+ },
47
+ {
48
+ "issues": [
49
+ {
50
+ "received": false,
51
+ "code": "invalid_literal",
52
+ "expected": "warn",
53
+ "path": [
54
+ "strictOptions",
55
+ "noPackageJsonTypings"
56
+ ],
57
+ "message": "Validation error: Invalid literal value, expected \\"warn\\" at \\"strictOptions.noPackageJsonTypings\\""
58
+ }
59
+ ],
60
+ "name": "ZodError"
61
+ },
62
+ {
63
+ "issues": [
64
+ {
65
+ "received": false,
66
+ "code": "invalid_literal",
67
+ "expected": "off",
68
+ "path": [
69
+ "strictOptions",
70
+ "noPackageJsonTypings"
71
+ ],
72
+ "message": "Validation error: Invalid literal value, expected \\"off\\" at \\"strictOptions.noPackageJsonTypings\\""
73
+ }
74
+ ],
75
+ "name": "ZodError"
76
+ }
77
+ ],
78
+ "path": [
79
+ "strictOptions",
80
+ "noPackageJsonTypings"
81
+ ],
82
+ "message": "Validation error: Validation error: Invalid literal value, expected \\"error\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\", or Validation error: Invalid literal value, expected \\"warn\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\", or Validation error: Invalid literal value, expected \\"off\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\""
83
+ }
84
+ ]]
85
+ `;
86
+
87
+ exports[`{'noPackageJsonTypings': true}} 1`] = `
88
+ [ZodError: [
89
+ {
90
+ "code": "invalid_union",
91
+ "unionErrors": [
92
+ {
93
+ "issues": [
94
+ {
95
+ "received": true,
96
+ "code": "invalid_literal",
97
+ "expected": "error",
98
+ "path": [
99
+ "strictOptions",
100
+ "noPackageJsonTypings"
101
+ ],
102
+ "message": "Validation error: Invalid literal value, expected \\"error\\" at \\"strictOptions.noPackageJsonTypings\\""
103
+ }
104
+ ],
105
+ "name": "ZodError"
106
+ },
107
+ {
108
+ "issues": [
109
+ {
110
+ "received": true,
111
+ "code": "invalid_literal",
112
+ "expected": "warn",
113
+ "path": [
114
+ "strictOptions",
115
+ "noPackageJsonTypings"
116
+ ],
117
+ "message": "Validation error: Invalid literal value, expected \\"warn\\" at \\"strictOptions.noPackageJsonTypings\\""
118
+ }
119
+ ],
120
+ "name": "ZodError"
121
+ },
122
+ {
123
+ "issues": [
124
+ {
125
+ "received": true,
126
+ "code": "invalid_literal",
127
+ "expected": "off",
128
+ "path": [
129
+ "strictOptions",
130
+ "noPackageJsonTypings"
131
+ ],
132
+ "message": "Validation error: Invalid literal value, expected \\"off\\" at \\"strictOptions.noPackageJsonTypings\\""
133
+ }
134
+ ],
135
+ "name": "ZodError"
136
+ }
137
+ ],
138
+ "path": [
139
+ "strictOptions",
140
+ "noPackageJsonTypings"
141
+ ],
142
+ "message": "Validation error: Validation error: Invalid literal value, expected \\"error\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\", or Validation error: Invalid literal value, expected \\"warn\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\", or Validation error: Invalid literal value, expected \\"off\\" at \\"strictOptions.noPackageJsonTypings\\" at \\"strictOptions.noPackageJsonTypings\\""
143
+ }
144
+ ]]
145
+ `;
@@ -2,8 +2,10 @@ import type {PluginItem as BabelPluginItem} from '@babel/core'
2
2
  import type {OptimizeLodashOptions} from '@optimize-lodash/rollup-plugin'
3
3
  import type {NormalizedOutputOptions, Plugin as RollupPlugin, TreeshakingOptions} from 'rollup'
4
4
 
5
+ import type {StrictOptions} from '../../strict'
6
+
5
7
  // re-export
6
- export type {RollupPlugin}
8
+ export type {RollupPlugin, StrictOptions}
7
9
 
8
10
  /** @public */
9
11
  export type PkgFormat = 'commonjs' | 'esm'
@@ -157,4 +159,8 @@ export interface PkgConfigOptions {
157
159
  */
158
160
  src?: string
159
161
  tsconfig?: string
162
+ /**
163
+ * Configure what checks are made when running `--strict` builds and checks
164
+ */
165
+ strictOptions?: StrictOptions
160
166
  }
@@ -1,3 +1,5 @@
1
+ import type {Logger} from '../../logger'
2
+ import type {InferredStrictOptions} from '../../strict'
1
3
  import {defaultEnding, fileEnding, legacyEnding} from '../../tasks/dts/getTargetPaths'
2
4
  import type {PkgExport} from '../config'
3
5
  import {isRecord} from '../isRecord'
@@ -9,12 +11,22 @@ import {validateExports} from './validateExports'
9
11
  export function parseExports(options: {
10
12
  pkg: PackageJSON
11
13
  strict: boolean
14
+ strictOptions: InferredStrictOptions
12
15
  legacyExports: boolean
16
+ logger: Logger
13
17
  }): (PkgExport & {_path: string})[] {
14
- const {pkg, strict, legacyExports} = options
18
+ const {pkg, strict, strictOptions, legacyExports, logger} = options
15
19
  const type = pkg.type || 'commonjs'
16
20
  const errors: string[] = []
17
21
 
22
+ const report = (kind: 'warn' | 'error', message: string) => {
23
+ if (kind === 'warn') {
24
+ logger.warn(message)
25
+ } else {
26
+ errors.push(message)
27
+ }
28
+ }
29
+
18
30
  if (pkg.source) {
19
31
  if (
20
32
  strict &&
@@ -145,8 +157,8 @@ export function parseExports(options: {
145
157
 
146
158
  // @TODO validate typesVersions when legacyExports is true
147
159
 
148
- if (strict && 'typings' in pkg) {
149
- errors.push('package.json: `typings` should be `types`')
160
+ if (strict && strictOptions.noPackageJsonTypings !== 'off' && 'typings' in pkg) {
161
+ report(strictOptions.noPackageJsonTypings, 'package.json: `typings` should be `types`')
150
162
  }
151
163
 
152
164
  if (strict && !pkg.types && pkg.source?.endsWith('.ts')) {
package/src/node/index.ts CHANGED
@@ -4,5 +4,6 @@ export * from './core'
4
4
  export * from './init'
5
5
  export * from './logger'
6
6
  export * from './resolveBuildTasks'
7
+ export {type InferredStrictOptions, strictOptions, toggle, type ToggleType} from './strict'
7
8
  export * from './tasks'
8
9
  export * from './watch'
@@ -17,6 +17,7 @@ import {findCommonDirPath, pathContains} from './core/findCommonPath'
17
17
  import type {Logger} from './logger'
18
18
  import {resolveBrowserTarget} from './resolveBrowserTarget'
19
19
  import {resolveNodeTarget} from './resolveNodeTarget'
20
+ import {parseStrictOptions} from './strict'
20
21
 
21
22
  export async function resolveBuildContext(options: {
22
23
  config?: PkgConfigOptions
@@ -37,24 +38,41 @@ export async function resolveBuildContext(options: {
37
38
  tsconfig: tsconfigPath,
38
39
  } = options
39
40
  const tsconfig = await loadTSConfig({cwd, tsconfigPath})
41
+ const strictOptions = parseStrictOptions(config?.strictOptions ?? {})
40
42
 
41
43
  /* eslint-disable padding-line-between-statements */
42
44
  let browserslist = pkg.browserslist
43
45
  if (!browserslist) {
44
- if (strict) {
45
- logger.warn(
46
- 'Could not detect a `browserslist` property in `package.json`, using default configuration. Add `"browserslist": "extends @sanity/browserslist-config"` to silence this warning.',
47
- )
46
+ if (strict && strictOptions.noImplicitBrowsersList !== 'off') {
47
+ if (strictOptions.noImplicitBrowsersList === 'error') {
48
+ throw new Error(
49
+ '\n- ' +
50
+ `package.json: "browserslist" is missing, set it to \`"browserslist": "extends @sanity/browserslist-config"\``,
51
+ )
52
+ } else {
53
+ logger.warn(
54
+ 'Could not detect a `browserslist` property in `package.json`, using default configuration. Add `"browserslist": "extends @sanity/browserslist-config"` to silence this warning.',
55
+ )
56
+ }
48
57
  }
49
58
  browserslist = DEFAULT_BROWSERSLIST_QUERY
50
59
  }
51
60
  const targetVersions = browserslistToEsbuild(browserslist)
52
61
  /* eslint-enable padding-line-between-statements */
53
62
 
54
- if (strict && typeof pkg.sideEffects === 'undefined') {
55
- logger.error(
56
- 'No `sideEffects` field in `package.json`, assuming all files are side-effectful. Add `"sideEffects": true` to silence this warning. See https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects for how to define `sideEffects`.',
57
- )
63
+ if (
64
+ strict &&
65
+ strictOptions.noImplicitSideEffects !== 'off' &&
66
+ typeof pkg.sideEffects === 'undefined'
67
+ ) {
68
+ const msg =
69
+ 'package.json: `sideEffects` is missing, see https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects for how to define `sideEffects`'
70
+
71
+ if (strictOptions.noImplicitSideEffects === 'error') {
72
+ throw new Error(msg)
73
+ } else {
74
+ logger.warn(msg)
75
+ }
58
76
  }
59
77
 
60
78
  const nodeTarget = resolveNodeTarget(targetVersions)
@@ -78,6 +96,8 @@ export async function resolveBuildContext(options: {
78
96
  pkg,
79
97
  strict,
80
98
  legacyExports: config?.legacyExports ?? false,
99
+ strictOptions,
100
+ logger,
81
101
  }).reduce<PkgExports>((acc, x) => {
82
102
  const {_path: exportPath, ...exportEntry} = x
83
103
 
@@ -0,0 +1,7 @@
1
+ import {expectTypeOf, test} from 'vitest'
2
+
3
+ import {InferredStrictOptions, StrictOptions} from './strict'
4
+
5
+ test('the zod schema types matches the manual types', () => {
6
+ expectTypeOf<StrictOptions>().toEqualTypeOf<Partial<InferredStrictOptions>>()
7
+ })
@@ -0,0 +1,66 @@
1
+ import {z} from 'zod'
2
+ import {errorMap} from 'zod-validation-error'
3
+
4
+ /**
5
+ * @public
6
+ */
7
+ export const toggle = z.union([z.literal('error'), z.literal('warn'), z.literal('off')])
8
+
9
+ /**
10
+ * @public
11
+ */
12
+ export type ToggleType = z.infer<typeof toggle>
13
+
14
+ /**
15
+ * @public
16
+ */
17
+ export const strictOptions = z
18
+ .object({
19
+ noPackageJsonTypings: toggle.default('error'),
20
+ noImplicitSideEffects: toggle.default('warn'),
21
+ noImplicitBrowsersList: toggle.default('warn'),
22
+ })
23
+ .strict()
24
+
25
+ /**
26
+ * To make error message paths line up with the paths in package.config.ts the schema is hoisted into a root schema
27
+ * This way errors will say `Expected boolean, received string at "strict.noPackageJsonTypings"` instead of `Expected boolean, received string at "noPackageJsonTypings"`.
28
+ */
29
+ const validationSchema = z.object({
30
+ strictOptions: strictOptions.default({}),
31
+ })
32
+
33
+ /**
34
+ * @public
35
+ */
36
+ export type InferredStrictOptions = z.infer<typeof strictOptions>
37
+
38
+ /**
39
+ * @public
40
+ */
41
+ export interface StrictOptions {
42
+ /**
43
+ * Disallows a top level `typings` field in `package.json` if it is equal to `exports['.'].source`.
44
+ * @defaultValue 'error'
45
+ */
46
+ noPackageJsonTypings?: ToggleType
47
+ /**
48
+ * Requires specifying `sideEffects` in `package.json`.
49
+ * @defaultValue 'warn'
50
+ */
51
+ noImplicitSideEffects?: ToggleType
52
+ /**
53
+ * Requires specifying `browserslist` in `package.json`, instead of relying on it implicitly being:
54
+ * @example
55
+ * ```
56
+ * "browserslist": "extends @sanity/browserslist-config"
57
+ * ```
58
+ * @defaultValue 'warn'
59
+ */
60
+ noImplicitBrowsersList?: ToggleType
61
+ }
62
+
63
+ /** @internal */
64
+ export function parseStrictOptions(input: unknown): InferredStrictOptions {
65
+ return validationSchema.parse({strictOptions: input}, {errorMap}).strictOptions
66
+ }