Skip to content

Commit

Permalink
main can only be commonjs, not esm
Browse files Browse the repository at this point in the history
Related discussion: isaacs/node-glob#557
  • Loading branch information
isaacs committed Sep 27, 2023
1 parent 3263369 commit 68d9726
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 130 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,25 +164,24 @@ package entry points.

By default, tshy deletes the `main` field, rather than maintain
this affordance for versions of node that met their end of life
more than a year ago. However, some tools still rely on `main`
and have not been updated to read the package entry points via
`exports`.
long ago. However, some tools still rely on `main` and have not
been updated to read the package entry points via `exports`.

**Warning: this will likely cause incorrect types to be loaded in
some scenarios.**

**Warning: this will likely cause incorrect types to be loaded.**
Use with extreme caution. It's almost always better to _not_
define top-level `main` and `types` fields if you are shipping a
hybrid module. Users will need to update their `module` and
`moduleResolution` tsconfigs appropriately. **That is a good
thing, and will save them future headaches.**

You can tell tshy to export a top-level `main` and `types` field
by setting `main` to either `commonjs` or `esm`. If `main` is set
to `"commonjs"`, then the package will not have `"type":
"module"` set.
by setting `main` to `true`.

If the specified dialect is not built, or if a `"."` export is
If the `commonjs` dialect is not built, or if a `"."` export is
not created, or if the `"."` export does not support the
specified dialect, then the build will fail.
`commonjs` dialect, then the build will fail.

For example, this config:

Expand All @@ -192,7 +191,7 @@ For example, this config:
"exports": {
".": "./src/index.ts"
},
"main": "commonjs"
"main": true
}
}
```
Expand All @@ -203,6 +202,7 @@ will produce:
{
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"type": "module",
"exports": {
".": {
"require": {
Expand Down
23 changes: 10 additions & 13 deletions src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import pkg from './package.js'
import polyfills from './polyfills.js'
import { resolveExport } from './resolve-export.js'
import { Export, Package, TshyConfig, TshyExport } from './types.js'
import { isDialect } from './valid-dialects.js'

export const getImpTarget = (
s: string | TshyExport | undefined | null
Expand Down Expand Up @@ -94,28 +93,26 @@ export const setMain = (
c: TshyConfig | undefined,
pkg: Package & { exports: Record<string, Export> }
) => {
if (c?.main !== undefined) {
if (!isDialect(c.main)) {
fail(`config.main must be 'commonjs' or 'esm', got: ${c.main}`)
return process.exit(1)
}
const m = c.main === 'commonjs' ? 'require' : 'import'
const mod = resolveExport(pkg.exports['.'], [m])
if (!!c?.main) {
c.main = true
const mod = resolveExport(pkg.exports['.'], ['require'])
if (!mod) {
fail(`could not resolve exports['.'] for tshy.main '${m}'`)
fail(`could not resolve exports['.'] for tshy.main 'require'`)
return process.exit(1)
}
const types = resolveExport(pkg.exports['.'], [m, 'types'])
const types = resolveExport(pkg.exports['.'], [
'require',
'types',
])
pkg.main = mod
if (c.main === 'esm') pkg.type = 'module'
else delete pkg.type
if (types && types !== mod) pkg.types = types
else delete pkg.types
} else {
pkg.type = 'module'
if (c) delete c.main
delete pkg.main
delete pkg.types
}
pkg.type = 'module'
}

// These are all defined by exports, so it's just confusing otherwise
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type TshyConfig = {
exports?: Record<string, TshyExport>
dialects?: Dialect[]
selfLink?: boolean
main?: Dialect
main?: boolean
}

export type Dialect = 'commonjs' | 'esm'
Expand Down
32 changes: 0 additions & 32 deletions tap-snapshots/test/exports.ts.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,3 @@ Array [
],
]
`

exports[`test/exports.ts > TAP > setting top level main > invalid main esm > must match snapshot 1`] = `
Array [
Array [
"could not resolve exports['.'] for tshy.main 'import'",
],
]
`

exports[`test/exports.ts > TAP > setting top level main > invalid main esm, no exports > must match snapshot 1`] = `
Array [
Array [
"could not resolve exports['.'] for tshy.main 'import'",
],
]
`

exports[`test/exports.ts > TAP > setting top level main > invalid main=blah > must match snapshot 1`] = `
Array [
Array [
"config.main must be 'commonjs' or 'esm', got: blah",
],
]
`

exports[`test/exports.ts > TAP > setting top level main > invalid main=false > must match snapshot 1`] = `
Array [
Array [
"config.main must be 'commonjs' or 'esm', got: false",
],
]
`
81 changes: 7 additions & 74 deletions test/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ t.test('setting top level main', async t => {
{
main?: string
types?: string
type?: string
},
boolean
][] = [
Expand All @@ -93,13 +92,13 @@ t.test('setting top level main', async t => {
},
},
},
{ type: 'module' },
{},
true,
],
[
'main commonjs',
{
tshy: { main: 'commonjs' },
tshy: { main: true },
exports: {
'.': {
require: { types: './r.d.ts', default: './r.js' },
Expand All @@ -110,24 +109,10 @@ t.test('setting top level main', async t => {
{ main: './r.js', types: './r.d.ts' },
true,
],
[
'main esm',
{
tshy: { main: 'esm' },
exports: {
'.': {
require: { types: './r.d.ts', default: './r.js' },
import: { types: './i.d.ts', default: './i.js' },
},
},
},
{ type: 'module', main: './i.js', types: './i.d.ts' },
true,
],
[
'main commonjs, no types',
{
tshy: { main: 'commonjs' },
tshy: { main: true },
exports: {
'.': {
require: './r.js',
Expand All @@ -138,39 +123,9 @@ t.test('setting top level main', async t => {
{ main: './r.js' },
true,
],
[
'main esm, no types',
{
tshy: { main: 'esm' },
exports: {
'.': {
require: { types: './r.d.ts', default: './r.js' },
import: './i.js',
},
},
},
{ main: './i.js', type: 'module' },
true,
],
[
'invalid main=blah',
{
//@ts-expect-error
tshy: { main: 'blah' },
exports: {
'.': {
require: { types: './r.d.ts', default: './r.js' },
import: { types: './i.d.ts', default: './i.js' },
},
},
},
{},
false,
],
[
'invalid main=false',
{
//@ts-expect-error
tshy: { main: false },
exports: {
'.': {
Expand All @@ -180,12 +135,12 @@ t.test('setting top level main', async t => {
},
},
{},
false,
true
],
[
'invalid main commonjs',
{
tshy: { main: 'commonjs' },
tshy: { main: true },
exports: {
'.': {
import: { types: './i.d.ts', default: './i.js' },
Expand All @@ -195,32 +150,10 @@ t.test('setting top level main', async t => {
{},
false,
],
[
'invalid main esm',
{
tshy: { main: 'esm' },
exports: {
'.': {
require: { types: './r.d.ts', default: './r.js' },
},
},
},
{},
false,
],
[
'invalid main commonjs, no exports',
{
tshy: { main: 'commonjs' },
exports: {},
},
{},
false,
],
[
'invalid main esm, no exports',
{
tshy: { main: 'esm' },
tshy: { main: true },
exports: {},
},
{},
Expand All @@ -243,7 +176,7 @@ t.test('setting top level main', async t => {
if (ok) {
t.equal(pkg.main, expect.main)
t.equal(pkg.types, expect.types)
t.equal(pkg.type, expect.type)
t.equal(pkg.type, 'module')
} else {
t.strictSame(exits(), [[1]])
t.matchSnapshot(fails)
Expand Down

0 comments on commit 68d9726

Please sign in to comment.