@sanity/pkg-utils 5.1.0 → 5.1.1

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": "5.1.0",
3
+ "version": "5.1.1",
4
4
  "description": "Simple utilities for modern npm packages.",
5
5
  "keywords": [
6
6
  "sanity-io",
@@ -25,7 +25,6 @@
25
25
  "type": "module",
26
26
  "exports": {
27
27
  ".": {
28
- "types": "./dist/index.d.ts",
29
28
  "source": "./src/node/index.ts",
30
29
  "import": "./dist/index.js",
31
30
  "require": "./dist/index.cjs",
@@ -133,7 +132,7 @@
133
132
  "typescript": "5.4.2",
134
133
  "vitest": "^1.4.0",
135
134
  "vitest-github-actions-reporter": "^0.11.1",
136
- "@sanity/pkg-utils": "5.1.0"
135
+ "@sanity/pkg-utils": "5.1.1"
137
136
  },
138
137
  "peerDependencies": {
139
138
  "typescript": "5.4.2"
package/src/node/build.ts CHANGED
@@ -3,7 +3,7 @@ import {createLogger} from './logger'
3
3
  import {resolveBuildContext} from './resolveBuildContext'
4
4
  import {resolveBuildTasks} from './resolveBuildTasks'
5
5
  import {createSpinner} from './spinner'
6
- import {BuildTask, buildTaskHandlers, TaskHandler} from './tasks'
6
+ import {type BuildTask, buildTaskHandlers, type TaskHandler} from './tasks'
7
7
 
8
8
  /**
9
9
  * Build the distribution files of a npm package.
package/src/node/check.ts CHANGED
@@ -41,14 +41,14 @@ export async function check(options: {
41
41
  if (exp.import && !fileExists(path.resolve(cwd, exp.import))) {
42
42
  missingFiles.push(exp.import)
43
43
  }
44
+ }
44
45
 
45
- if (exp.types && !fileExists(path.resolve(cwd, exp.types))) {
46
- missingFiles.push(exp.types)
47
- }
46
+ if (ctx.pkg.types && !fileExists(path.resolve(cwd, ctx.pkg.types))) {
47
+ missingFiles.push(ctx.pkg.types)
48
48
  }
49
49
 
50
50
  if (missingFiles.length) {
51
- logger.error('missing files')
51
+ logger.error(`missing files: ${missingFiles.join(', ')}`)
52
52
  process.exit(1)
53
53
  }
54
54
 
@@ -1,10 +1,10 @@
1
1
  import chalk from 'chalk'
2
2
  import {ZodError} from 'zod'
3
3
 
4
- import {Logger} from '../../logger'
5
- import {assertFirst, assertLast, assertOrder} from './helpers'
4
+ import type {Logger} from '../../logger'
5
+ import {assertLast, assertOrder} from './helpers'
6
6
  import {loadPkg} from './loadPkg'
7
- import {PackageJSON} from './types'
7
+ import type {PackageJSON} from './types'
8
8
 
9
9
  /** @internal */
10
10
  export async function loadPkgWithReporting(options: {
@@ -28,9 +28,11 @@ export async function loadPkgWithReporting(options: {
28
28
 
29
29
  const keys = Object.keys(exp)
30
30
 
31
- if (!assertFirst('types', keys)) {
31
+ if (exp.types) {
32
32
  shouldError = true
33
- logger.error(`exports["${expPath}"]: the \`types\` property should be the first property`)
33
+ logger.error(
34
+ `exports["${expPath}"]: the \`types\` condition shouldn't be used as dts files are generated in such a way that both CJS and ESM is supported`,
35
+ )
34
36
  }
35
37
 
36
38
  if (exp.module) {
@@ -1,7 +1,7 @@
1
- import {PkgExport} from '../config'
1
+ import type {PkgExport} from '../config'
2
2
  import {isRecord} from '../isRecord'
3
- import {PkgExtMap} from './pkgExt'
4
- import {PackageJSON} from './types'
3
+ import type {PkgExtMap} from './pkgExt'
4
+ import type {PackageJSON} from './types'
5
5
  import {validateExports} from './validateExports'
6
6
 
7
7
  /** @internal */
@@ -15,7 +15,6 @@ export function parseExports(options: {
15
15
  const rootExport: PkgExport & {_path: string} = {
16
16
  _exported: true,
17
17
  _path: '.',
18
- types: pkg.types,
19
18
  source: pkg.source || '',
20
19
  browser: pkg.browser && {
21
20
  source: pkg.source || '',
@@ -31,6 +30,16 @@ export function parseExports(options: {
31
30
 
32
31
  const errors: string[] = []
33
32
 
33
+ if (strict && !pkg.types && pkg.source?.endsWith('.ts')) {
34
+ errors.push(
35
+ 'package.json: `types` must be declared for the npm listing to show as a TypeScript module.',
36
+ )
37
+ }
38
+
39
+ if (strict && 'typings' in pkg) {
40
+ errors.push('package.json: `typings` should be `types`')
41
+ }
42
+
34
43
  if (pkg.exports) {
35
44
  if (strict && !pkg.exports['./package.json']) {
36
45
  errors.push('package.json: `exports["./package.json"] must be declared.')
@@ -63,12 +72,6 @@ export function parseExports(options: {
63
72
  )
64
73
  }
65
74
 
66
- if (exportEntry.types && rootExport.types && exportEntry.types !== rootExport.types) {
67
- errors.push(
68
- 'package.json: mismatch between "types" and "exports.types". These must be equal.',
69
- )
70
- }
71
-
72
75
  if (exportEntry.source && rootExport.source && exportEntry.source !== rootExport.source) {
73
76
  errors.push(
74
77
  'package.json: mismatch between "source" and "exports.source". These must be equal.',
@@ -1,8 +1,8 @@
1
- import {ExtractorMessage} from '@microsoft/api-extractor'
1
+ import type {ExtractorMessage} from '@microsoft/api-extractor'
2
2
  import chalk from 'chalk'
3
3
  import path from 'path'
4
4
 
5
- import {BuildContext} from './core'
5
+ import type {BuildContext} from './core'
6
6
 
7
7
  export function printExtractMessages(ctx: BuildContext, messages: ExtractorMessage[]): void {
8
8
  const {cwd, logger} = ctx
@@ -49,7 +49,6 @@ export function printPackageTree(ctx: BuildContext): void {
49
49
  .filter(([, entry]) => entry._exported)
50
50
  .map(([exportPath, entry]) => {
51
51
  const exp: Omit<PkgExport, '_exported'> = {
52
- types: undefined,
53
52
  source: fileInfo(entry.source),
54
53
  browser: undefined,
55
54
  require: undefined,
@@ -58,12 +57,6 @@ export function printPackageTree(ctx: BuildContext): void {
58
57
  default: fileInfo(entry.default),
59
58
  }
60
59
 
61
- if (entry.types) {
62
- exp.types = fileInfo(entry.types)
63
- } else {
64
- delete exp.types
65
- }
66
-
67
60
  if (entry.browser) {
68
61
  exp.browser = {source: fileInfo(entry.browser.source)}
69
62
 
@@ -3,6 +3,7 @@ import path from 'path'
3
3
 
4
4
  import type {BuildContext, PkgExport, PkgFormat, PkgRuntime} from './core'
5
5
  import type {BuildTask, DtsTask, RollupTask, RollupTaskEntry} from './tasks'
6
+ import {getTargetPaths} from './tasks/dts/getTargetPaths'
6
7
 
7
8
  /** @internal */
8
9
  export function resolveBuildTasks(ctx: BuildContext): BuildTask[] {
@@ -44,12 +45,48 @@ export function resolveBuildTasks(ctx: BuildContext): BuildTask[] {
44
45
  for (const exp of exports) {
45
46
  const importId = path.join(pkg.name, exp._path)
46
47
 
47
- if (exp.types) {
48
+ if (exp.source?.endsWith('.ts')) {
48
49
  dtsTask.entries.push({
49
50
  importId,
50
51
  exportPath: exp._path,
51
52
  sourcePath: exp.source,
52
- targetPath: exp.types,
53
+ targetPaths: getTargetPaths(pkg.type, exp),
54
+ })
55
+ }
56
+
57
+ if (exp.browser?.source?.endsWith('.ts')) {
58
+ dtsTask.entries.push({
59
+ importId,
60
+ exportPath: exp._path,
61
+ sourcePath: exp.browser.source,
62
+ targetPaths: getTargetPaths(pkg.type, exp.browser),
63
+ })
64
+ }
65
+
66
+ if (exp.node?.source?.endsWith('.ts')) {
67
+ dtsTask.entries.push({
68
+ importId,
69
+ exportPath: exp._path,
70
+ sourcePath: exp.node.source,
71
+ targetPaths: getTargetPaths(pkg.type, exp.node),
72
+ })
73
+ }
74
+ }
75
+
76
+ // Handle dts tasks for bundles
77
+ for (const bundle of bundles) {
78
+ if (bundle.source?.endsWith('.ts')) {
79
+ // importId needs to be how the bundle is used, like `@sanity/pkg-utils/dist/cli`
80
+ // exportPath needs to be the path to the bundle, like `./dist/cli`
81
+ // targetPaths is then: [./dist/cli.d.ts, ./dist/cli.d.cts]
82
+ const exportPath = (bundle.import || bundle.require)!.replace(/\.[mc]?js$/, '')
83
+ const importId = path.join(pkg.name, exportPath)
84
+
85
+ dtsTask.entries.push({
86
+ importId,
87
+ exportPath,
88
+ sourcePath: bundle.source,
89
+ targetPaths: getTargetPaths(pkg.type, bundle),
53
90
  })
54
91
  }
55
92
  }
@@ -1,8 +1,9 @@
1
1
  import fs from 'fs'
2
2
  import path from 'path'
3
3
 
4
- import {BuildContext, PkgExport, PkgFormat, PkgRuntime} from './core'
5
- import {DtsWatchTask, RollupTaskEntry, RollupWatchTask, WatchTask} from './tasks'
4
+ import type {BuildContext, PkgExport, PkgFormat, PkgRuntime} from './core'
5
+ import type {DtsWatchTask, RollupTaskEntry, RollupWatchTask, WatchTask} from './tasks'
6
+ import {getTargetPaths} from './tasks/dts/getTargetPaths'
6
7
 
7
8
  /** @internal */
8
9
  export function resolveWatchTasks(ctx: BuildContext): WatchTask[] {
@@ -41,12 +42,30 @@ export function resolveWatchTasks(ctx: BuildContext): WatchTask[] {
41
42
  for (const exp of exports) {
42
43
  const importId = path.join(pkg.name, exp._path)
43
44
 
44
- if (exp.types) {
45
+ if (exp.source?.endsWith('.ts')) {
45
46
  dtsTask.entries.push({
46
47
  importId,
47
48
  exportPath: exp._path,
48
49
  sourcePath: exp.source,
49
- targetPath: exp.types,
50
+ targetPaths: getTargetPaths(pkg.type, exp),
51
+ })
52
+ }
53
+
54
+ if (exp.browser?.source?.endsWith('.ts')) {
55
+ dtsTask.entries.push({
56
+ importId,
57
+ exportPath: exp._path,
58
+ sourcePath: exp.browser.source,
59
+ targetPaths: getTargetPaths(pkg.type, exp.browser),
60
+ })
61
+ }
62
+
63
+ if (exp.node?.source?.endsWith('.ts')) {
64
+ dtsTask.entries.push({
65
+ importId,
66
+ exportPath: exp._path,
67
+ sourcePath: exp.node.source,
68
+ targetPaths: getTargetPaths(pkg.type, exp.node),
50
69
  })
51
70
  }
52
71
  }
@@ -34,7 +34,7 @@ export async function doExtract(
34
34
  await buildTypes({cwd, logger, outDir: tmpPath, strict, tsconfig: ts.config})
35
35
  const messages: ExtractorMessage[] = []
36
36
 
37
- const results: {sourcePath: string; filePath: string}[] = []
37
+ const results: {sourcePath: string; filePaths: string[]}[] = []
38
38
 
39
39
  for (const entry of task.entries) {
40
40
  const exportPath = entry.exportPath === '.' ? './index' : entry.exportPath
@@ -44,8 +44,8 @@ export async function doExtract(
44
44
  path.relative(rootDir, path.resolve(cwd, entry.sourcePath)).replace(/\.ts$/, '.d.ts'),
45
45
  )
46
46
 
47
- const targetPath = path.resolve(cwd, entry.targetPath)
48
- const filePath = path.relative(outDir, targetPath)
47
+ const targetPaths = entry.targetPaths.map((targetPath) => path.resolve(cwd, targetPath))
48
+ const filePaths = targetPaths.map((targetPath) => path.relative(outDir, targetPath))
49
49
  const result = await extractTypes({
50
50
  bundledPackages: config?.extract?.bundledPackages || [],
51
51
  customTags: config?.extract?.customTags,
@@ -53,7 +53,7 @@ export async function doExtract(
53
53
  distPath: outDir,
54
54
  exportPath,
55
55
  files,
56
- filePath: filePath,
56
+ filePaths,
57
57
  projectPath: cwd,
58
58
  rules: config?.extract?.rules,
59
59
  sourceTypesPath: sourceTypesPath,
@@ -71,7 +71,7 @@ export async function doExtract(
71
71
  throw new DtsError(`encountered ${errors.length} errors when extracting types`, errors)
72
72
  }
73
73
 
74
- results.push({sourcePath: path.resolve(cwd, entry.sourcePath), filePath: targetPath})
74
+ results.push({sourcePath: path.resolve(cwd, entry.sourcePath), filePaths: targetPaths})
75
75
  }
76
76
 
77
77
  await rimraf(tmpPath)
@@ -13,12 +13,16 @@ export const dtsTask: TaskHandler<DtsTask, DtsResult> = {
13
13
  [
14
14
  'Build type definitions...',
15
15
  ' entries:',
16
- ...task.entries.map((entry) =>
17
- [
18
- ` - ${chalk.cyan(entry.importId)}: `,
19
- `${chalk.yellow(entry.sourcePath)} ${chalk.gray('→')} ${chalk.yellow(entry.targetPath)}`,
20
- ].join(''),
21
- ),
16
+ ...task.entries.map((entry) => {
17
+ return entry.targetPaths
18
+ .map((targetPath) => {
19
+ return [
20
+ ` - ${chalk.cyan(entry.importId)}: `,
21
+ `${chalk.yellow(entry.sourcePath)} ${chalk.gray('→')} ${chalk.yellow(targetPath)}`,
22
+ ].join('')
23
+ })
24
+ .join('\n')
25
+ }),
22
26
  ].join('\n'),
23
27
  exec: (ctx, task) => {
24
28
  return new Observable((observer) => {
@@ -2,20 +2,24 @@ import chalk from 'chalk'
2
2
  import {Observable} from 'rxjs'
3
3
 
4
4
  import {printExtractMessages} from '../../printExtractMessages'
5
- import {TaskHandler} from '../types'
5
+ import type {TaskHandler} from '../types'
6
6
  import {doExtract} from './doExtract'
7
7
  import {DtsError} from './DtsError'
8
- import {DtsResult, DtsWatchTask} from './types'
8
+ import type {DtsResult, DtsWatchTask} from './types'
9
9
 
10
10
  /** @internal */
11
11
  export const dtsWatchTask: TaskHandler<DtsWatchTask, DtsResult> = {
12
12
  name: (_ctx, task) =>
13
13
  [
14
14
  `build type definitions`,
15
- ...task.entries.map(
16
- (entry) =>
17
- ` ${chalk.blue(entry.importId)}: ${entry.sourcePath} -> ${entry.targetPath}`,
18
- ),
15
+ ...task.entries.map((entry) => {
16
+ return entry.targetPaths.map((targetPath) => {
17
+ return [
18
+ ` - ${chalk.cyan(entry.importId)}: `,
19
+ `${chalk.yellow(entry.sourcePath)} ${chalk.gray('→')} ${chalk.yellow(targetPath)}`,
20
+ ].join('')
21
+ })
22
+ }),
19
23
  ].join('\n'),
20
24
  exec: (ctx, task) => {
21
25
  return new Observable((observer) => {
@@ -8,7 +8,7 @@ import fs from 'fs/promises'
8
8
  import {mkdirp} from 'mkdirp'
9
9
  import path from 'path'
10
10
  import prettier from 'prettier'
11
- import ts from 'typescript'
11
+ import type ts from 'typescript'
12
12
 
13
13
  import type {BuildFile, PkgConfigOptions} from '../../core'
14
14
  import {createApiExtractorConfig} from './createApiExtractorConfig'
@@ -22,7 +22,7 @@ export async function extractTypes(options: {
22
22
  cwd: string
23
23
  distPath: string
24
24
  exportPath: string
25
- filePath: string
25
+ filePaths: string[]
26
26
  files: BuildFile[]
27
27
  projectPath: string
28
28
  rules?: NonNullable<PkgConfigOptions['extract']>['rules']
@@ -37,7 +37,7 @@ export async function extractTypes(options: {
37
37
  distPath,
38
38
  exportPath,
39
39
  files,
40
- filePath,
40
+ filePaths,
41
41
  projectPath,
42
42
  rules,
43
43
  sourceTypesPath,
@@ -50,6 +50,9 @@ export async function extractTypes(options: {
50
50
  customTags: customTags || [],
51
51
  })
52
52
 
53
+ const filePath = filePaths[0].replace(/\.d\.[mc]ts$/, '.d.ts')
54
+ // If there are package.config.ts `bundles` we might not have something that should leave behind a `.d.ts` file and need to handle that
55
+ const shouldCleanUpDts = !filePaths.includes(filePath)
53
56
  const extractorConfig: ExtractorConfig = ExtractorConfig.prepare({
54
57
  configObject: createApiExtractorConfig({
55
58
  bundledPackages,
@@ -94,20 +97,26 @@ export async function extractTypes(options: {
94
97
  })
95
98
 
96
99
  const code = [typesBuf.toString(), ...moduleBlocks].join('\n\n')
100
+ const prettyCode = await prettier.format(code, {
101
+ ...prettierConfig,
102
+ filepath: typesPath,
103
+ })
97
104
 
98
- await fs.writeFile(
99
- typesPath,
100
- await prettier.format(code, {
101
- ...prettierConfig,
102
- filepath: typesPath,
103
- }),
104
- )
105
+ for (const expFilePath of filePaths) {
106
+ const expTypesPath = path.resolve(distPath, expFilePath)
105
107
 
106
- // Add to `files` in context
107
- files.push({
108
- type: 'types',
109
- path: typesPath,
110
- })
108
+ await fs.writeFile(expTypesPath, prettyCode)
109
+
110
+ // Add to `files` in context
111
+ files.push({
112
+ type: 'types',
113
+ path: expTypesPath,
114
+ })
115
+ }
116
+
117
+ if (shouldCleanUpDts) {
118
+ await fs.unlink(typesPath)
119
+ }
111
120
 
112
121
  return {extractorResult, messages}
113
122
  }
@@ -0,0 +1,64 @@
1
+ import type {PackageJSON, PkgBundle, PkgExport} from '../../core'
2
+
3
+ const fileEnding = /\.[mc]?js$/
4
+ const dtsEnding = '.d.ts'
5
+ const mtsEnding = '.d.mts'
6
+ const ctsEnding = '.d.cts'
7
+
8
+ /** @internal */
9
+ export function getTargetPaths(
10
+ _type: PackageJSON['type'],
11
+ expOrBundle: PkgExport | PkgExport['browser'] | PkgExport['node'] | PkgBundle,
12
+ ): string[] {
13
+ const type = (_type === 'module' ? 'module' : 'commonjs') satisfies PackageJSON['type']
14
+
15
+ const set = new Set<string>()
16
+
17
+ if (expOrBundle?.import) {
18
+ set.add(expOrBundle.import.replace(fileEnding, type === 'module' ? dtsEnding : mtsEnding))
19
+ }
20
+
21
+ if (expOrBundle?.require) {
22
+ set.add(expOrBundle.require.replace(fileEnding, type === 'commonjs' ? dtsEnding : ctsEnding))
23
+ }
24
+
25
+ if (isPkgExport(expOrBundle)) {
26
+ if (!expOrBundle.browser?.source) {
27
+ if (expOrBundle.browser?.import) {
28
+ set.add(
29
+ expOrBundle.browser.import.replace(fileEnding, type === 'module' ? dtsEnding : mtsEnding),
30
+ )
31
+ }
32
+
33
+ if (expOrBundle.browser?.require) {
34
+ set.add(
35
+ expOrBundle.browser.require.replace(
36
+ fileEnding,
37
+ type === 'commonjs' ? dtsEnding : ctsEnding,
38
+ ),
39
+ )
40
+ }
41
+ }
42
+
43
+ if (!expOrBundle.node?.source) {
44
+ if (expOrBundle.node?.import) {
45
+ set.add(
46
+ expOrBundle.node.import.replace(fileEnding, type === 'module' ? dtsEnding : mtsEnding),
47
+ )
48
+ }
49
+
50
+ if (expOrBundle.node?.require) {
51
+ set.add(
52
+ expOrBundle.node.require.replace(fileEnding, type === 'commonjs' ? dtsEnding : ctsEnding),
53
+ )
54
+ }
55
+ }
56
+ }
57
+
58
+ return Array.from(set)
59
+ }
60
+
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ function isPkgExport(exp: any): exp is PkgExport {
63
+ return exp?.browser || exp?.node || exp?.default
64
+ }
@@ -1,4 +1,4 @@
1
- import {ExtractorMessage} from '@microsoft/api-extractor'
1
+ import type {ExtractorMessage} from '@microsoft/api-extractor'
2
2
 
3
3
  /** @internal */
4
4
  export interface DtsWatchTask {
@@ -7,7 +7,7 @@ export interface DtsWatchTask {
7
7
  exportPath: string
8
8
  importId: string
9
9
  sourcePath: string
10
- targetPath: string
10
+ targetPaths: string[]
11
11
  }[]
12
12
  }
13
13
 
@@ -18,7 +18,7 @@ export interface DtsTask {
18
18
  exportPath: string
19
19
  importId: string
20
20
  sourcePath: string
21
- targetPath: string
21
+ targetPaths: string[]
22
22
  }[]
23
23
  }
24
24
 
@@ -26,5 +26,5 @@ export interface DtsTask {
26
26
  export interface DtsResult {
27
27
  type: 'dts'
28
28
  messages: ExtractorMessage[]
29
- results: {sourcePath: string; filePath: string}[]
29
+ results: {sourcePath: string; filePaths: string[]}[]
30
30
  }
@@ -278,12 +278,6 @@ export const defaultTemplate: PkgTemplate = async ({cwd, logger, packagePath}) =
278
278
  if (features.typescript) {
279
279
  pkgJson.types = './dist/index.d.ts'
280
280
 
281
- const mainExport = pkgJson.exports?.['.']
282
-
283
- if (isRecord(mainExport)) {
284
- mainExport.types = './dist/index.d.ts'
285
- }
286
-
287
281
  pkgJson.scripts = {
288
282
  ...pkgJson.scripts,
289
283
  ['ts:check']: 'tsc --build',