@@ -902,273 +902,7 @@ $ node other.js
902902
903903## Dual CommonJS/ES module packages  
904904
905- <!--  This section should not be in the API documentation:
906- 
907- 1. It teaches opinionated practices that some consider dangerous, see 
908-    https://github.com/nodejs/node/issues/52174 
909- 2. It will soon be obsolete when we unflag --experimental-require-module. 
910- 3. It's difficult to understand a multi-file structure via long texts and snippets in 
911-    a markdown document. 
912- 
913- TODO(?): Move this section to its own repository with example folders. 
914- --> 
915- 
916- Prior to the introduction of support for ES modules in Node.js, it was a common
917- pattern for package authors to include both CommonJS and ES module JavaScript
918- sources in their package, with ` package.json `  [ ` "main" ` ] [ ]  specifying the
919- CommonJS entry point and ` package.json `  ` "module" `  specifying the ES module
920- entry point.
921- This enabled Node.js to run the CommonJS entry point while build tools such as
922- bundlers used the ES module entry point, since Node.js ignored (and still
923- ignores) the top-level ` "module" `  field.
924- 
925- Node.js can now run ES module entry points, and a package can contain both
926- CommonJS and ES module entry points (either via separate specifiers such as
927- ` 'pkg' `  and ` 'pkg/es-module' ` , or both at the same specifier via [ Conditional
928- exports] [ ] ). Unlike in the scenario where top-level ` "module" `  field is only used by bundlers,
929- or ES module files are transpiled into CommonJS on the fly before evaluation by
930- Node.js, the files referenced by the ES module entry point are evaluated as ES
931- modules.
932- 
933- ### Dual package hazard  
934- 
935- When an application is using a package that provides both CommonJS and ES module
936- sources, there is a risk of certain bugs if both versions of the package get
937- loaded. This potential comes from the fact that the ` pkgInstance `  created by
938- ` const pkgInstance = require('pkg') `  is not the same as the ` pkgInstance ` 
939- created by ` import pkgInstance from 'pkg' `  (or an alternative main path like
940- ` 'pkg/module' ` ). This is the “dual package hazard,” where two versions of the
941- same package can be loaded within the same runtime environment. While it is
942- unlikely that an application or package would intentionally load both versions
943- directly, it is common for an application to load one version while a dependency
944- of the application loads the other version. This hazard can happen because
945- Node.js supports intermixing CommonJS and ES modules, and can lead to unexpected
946- behavior.
947- 
948- If the package main export is a constructor, an ` instanceof `  comparison of
949- instances created by the two versions returns ` false ` , and if the export is an
950- object, properties added to one (like ` pkgInstance.foo = 3 ` ) are not present on
951- the other. This differs from how ` import `  and ` require `  statements work in
952- all-CommonJS or all-ES module environments, respectively, and therefore is
953- surprising to users. It also differs from the behavior users are familiar with
954- when using transpilation via tools like [ Babel] [ ]  or [ ` esm ` ] [ ] .
955- 
956- ### Writing dual packages while avoiding or minimizing hazards  
957- 
958- First, the hazard described in the previous section occurs when a package
959- contains both CommonJS and ES module sources and both sources are provided for
960- use in Node.js, either via separate main entry points or exported paths. A
961- package might instead be written where any version of Node.js receives only
962- CommonJS sources, and any separate ES module sources the package might contain
963- are intended only for other environments such as browsers. Such a package
964- would be usable by any version of Node.js, since ` import `  can refer to CommonJS
965- files; but it would not provide any of the advantages of using ES module syntax.
966- 
967- A package might also switch from CommonJS to ES module syntax in a [ breaking
968- change] ( https://semver.org/ )  version bump. This has the disadvantage that the
969- newest version of the package would only be usable in ES module-supporting
970- versions of Node.js.
971- 
972- Every pattern has tradeoffs, but there are two broad approaches that satisfy the
973- following conditions:
974- 
975- 1 .  The package is usable via both ` require `  and ` import ` .
976- 2 .  The package is usable in both current Node.js and older versions of Node.js
977-    that lack support for ES modules.
978- 3 .  The package main entry point, e.g. ` 'pkg' `  can be used by both ` require `  to
979-    resolve to a CommonJS file and by ` import `  to resolve to an ES module file.
980-    (And likewise for exported paths, e.g. ` 'pkg/feature' ` .)
981- 4 .  The package provides named exports, e.g. ` import { name } from 'pkg' `  rather
982-    than ` import pkg from 'pkg'; pkg.name ` .
983- 5 .  The package is potentially usable in other ES module environments such as
984-    browsers.
985- 6 .  The hazards described in the previous section are avoided or minimized.
986- 
987- #### Approach #1  : Use an ES module wrapper  
988- 
989- Write the package in CommonJS or transpile ES module sources into CommonJS, and
990- create an ES module wrapper file that defines the named exports. Using
991- [ Conditional exports] [ ] , the ES module wrapper is used for ` import `  and the
992- CommonJS entry point for ` require ` .
993- 
994- ``` json 
995- // ./node_modules/pkg/package.json 
996- {
997-   "type" : " module"  ,
998-   "exports" : {
999-     "import" : " ./wrapper.mjs"  ,
1000-     "require" : " ./index.cjs" 
1001-   }
1002- }
1003- ``` 
1004- 
1005- The preceding example uses explicit extensions ` .mjs `  and ` .cjs ` .
1006- If your files use the ` .js `  extension, ` "type": "module" `  will cause such files
1007- to be treated as ES modules, just as ` "type": "commonjs" `  would cause them
1008- to be treated as CommonJS.
1009- See [ Enabling] ( esm.md#enabling ) .
1010- 
1011- ``` cjs 
1012- //  ./node_modules/pkg/index.cjs
1013- exports .name  =  ' value'  ;
1014- ``` 
1015- 
1016- ``` js 
1017- //  ./node_modules/pkg/wrapper.mjs
1018- import  cjsModule  from  ' ./index.cjs'  ;
1019- export  const  name  =  cjsModule .name ;
1020- ``` 
1021- 
1022- In this example, the ` name `  from ` import { name } from 'pkg' `  is the same
1023- singleton as the ` name `  from ` const { name } = require('pkg') ` . Therefore ` === ` 
1024- returns ` true `  when comparing the two ` name ` s and the divergent specifier hazard
1025- is avoided.
1026- 
1027- If the module is not simply a list of named exports, but rather contains a
1028- unique function or object export like ` module.exports = function () { ... } ` ,
1029- or if support in the wrapper for the ` import pkg from 'pkg' `  pattern is desired,
1030- then the wrapper would instead be written to export the default optionally
1031- along with any named exports as well:
1032- 
1033- ``` js 
1034- import  cjsModule  from  ' ./index.cjs'  ;
1035- export  const  name  =  cjsModule .name ;
1036- export  default  cjsModule ;
1037- ``` 
1038- 
1039- This approach is appropriate for any of the following use cases:
1040- 
1041- *  The package is currently written in CommonJS and the author would prefer not
1042-   to refactor it into ES module syntax, but wishes to provide named exports for
1043-   ES module consumers.
1044- *  The package has other packages that depend on it, and the end user might
1045-   install both this package and those other packages. For example a ` utilities ` 
1046-   package is used directly in an application, and a ` utilities-plus `  package
1047-   adds a few more functions to ` utilities ` . Because the wrapper exports
1048-   underlying CommonJS files, it doesn't matter if ` utilities-plus `  is written in
1049-   CommonJS or ES module syntax; it will work either way.
1050- *  The package stores internal state, and the package author would prefer not to
1051-   refactor the package to isolate its state management. See the next section.
1052- 
1053- A variant of this approach not requiring conditional exports for consumers could
1054- be to add an export, e.g. ` "./module" ` , to point to an all-ES module-syntax
1055- version of the package. This could be used via ` import 'pkg/module' `  by users
1056- who are certain that the CommonJS version will not be loaded anywhere in the
1057- application, such as by dependencies; or if the CommonJS version can be loaded
1058- but doesn't affect the ES module version (for example, because the package is
1059- stateless):
1060- 
1061- ``` json 
1062- // ./node_modules/pkg/package.json 
1063- {
1064-   "type" : " module"  ,
1065-   "exports" : {
1066-     "." : " ./index.cjs"  ,
1067-     "./module" : " ./wrapper.mjs" 
1068-   }
1069- }
1070- ``` 
1071- 
1072- #### Approach #2  : Isolate state  
1073- 
1074- A [ ` package.json ` ] [ ]  file can define the separate CommonJS and ES module entry
1075- points directly:
1076- 
1077- ``` json 
1078- // ./node_modules/pkg/package.json 
1079- {
1080-   "type" : " module"  ,
1081-   "exports" : {
1082-     "import" : " ./index.mjs"  ,
1083-     "require" : " ./index.cjs" 
1084-   }
1085- }
1086- ``` 
1087- 
1088- This can be done if both the CommonJS and ES module versions of the package are
1089- equivalent, for example because one is the transpiled output of the other; and
1090- the package's management of state is carefully isolated (or the package is
1091- stateless).
1092- 
1093- The reason that state is an issue is because both the CommonJS and ES module
1094- versions of the package might get used within an application; for example, the
1095- user's application code could ` import `  the ES module version while a dependency
1096- ` require ` s the CommonJS version. If that were to occur, two copies of the
1097- package would be loaded in memory and therefore two separate states would be
1098- present. This would likely cause hard-to-troubleshoot bugs.
1099- 
1100- Aside from writing a stateless package (if JavaScript's ` Math `  were a package,
1101- for example, it would be stateless as all of its methods are static), there are
1102- some ways to isolate state so that it's shared between the potentially loaded
1103- CommonJS and ES module instances of the package:
1104- 
1105- 1 .  If possible, contain all state within an instantiated object. JavaScript's
1106-    ` Date ` , for example, needs to be instantiated to contain state; if it were a
1107-    package, it would be used like this:
1108- 
1109-    ``` js 
1110-    import  Date  from  ' date'  ;
1111-    const  someDate  =  new  Date ();
1112-    //  someDate contains state; Date does not
1113-    ``` 
1114- 
1115-    The ` new `  keyword isn't required; a package's function can return a new
1116-    object, or modify a passed-in object, to keep the state external to the
1117-    package.
1118- 
1119- 2 .  Isolate the state in one or more CommonJS files that are shared between the
1120-    CommonJS and ES module versions of the package. For example, if the CommonJS
1121-    and ES module entry points are ` index.cjs `  and ` index.mjs ` , respectively:
1122- 
1123-    ``` cjs 
1124-    //  ./node_modules/pkg/index.cjs
1125-    const  state  =  require (' ./state.cjs'  );
1126-    module .exports .state  =  state;
1127-    ``` 
1128- 
1129-    ``` js 
1130-    //  ./node_modules/pkg/index.mjs
1131-    import  state  from  ' ./state.cjs'  ;
1132-    export  {
1133-      state ,
1134-    };
1135-    ``` 
1136- 
1137-    Even if ` pkg `  is used via both ` require `  and ` import `  in an application (for
1138-    example, via ` import `  in application code and via ` require `  by a dependency)
1139-    each reference of ` pkg `  will contain the same state; and modifying that
1140-    state from either module system will apply to both.
1141- 
1142- Any plugins that attach to the package's singleton would need to separately
1143- attach to both the CommonJS and ES module singletons.
1144- 
1145- This approach is appropriate for any of the following use cases:
1146- 
1147- *  The package is currently written in ES module syntax and the package author
1148-   wants that version to be used wherever such syntax is supported.
1149- *  The package is stateless or its state can be isolated without too much
1150-   difficulty.
1151- *  The package is unlikely to have other public packages that depend on it, or if
1152-   it does, the package is stateless or has state that need not be shared between
1153-   dependencies or with the overall application.
1154- 
1155- Even with isolated state, there is still the cost of possible extra code
1156- execution between the CommonJS and ES module versions of a package.
1157- 
1158- As with the previous approach, a variant of this approach not requiring
1159- conditional exports for consumers could be to add an export, e.g.
1160- ` "./module" ` , to point to an all-ES module-syntax version of the package:
1161- 
1162- ``` json 
1163- // ./node_modules/pkg/package.json 
1164- {
1165-   "type" : " module"  ,
1166-   "exports" : {
1167-     "." : " ./index.cjs"  ,
1168-     "./module" : " ./index.mjs" 
1169-   }
1170- }
1171- ``` 
905+ See [ the package examples repository] [ ]  for details.
1172906
1173907## Node.js ` package.json `  field definitions  
1174908
@@ -1412,7 +1146,6 @@ Package imports permit mapping to external packages.
14121146
14131147This field defines [ subpath imports] [ ]  for the current package.
14141148
1415- [ Babel ] : https://babeljs.io/ 
14161149[ CommonJS ] : modules.md 
14171150[ Conditional exports ] : #conditional-exports 
14181151[ Corepack ] : corepack.md 
@@ -1432,7 +1165,6 @@ This field defines [subpath imports][] for the current package.
14321165[ `--experimental-default-type` ] : cli.md#--experimental-default-typetype 
14331166[ `--no-addons` flag ] : cli.md#--no-addons 
14341167[ `ERR_PACKAGE_PATH_NOT_EXPORTED` ] : errors.md#err_package_path_not_exported 
1435- [ `esm` ] : https://github.com/standard-things/esm#readme 
14361168[ `package.json` ] : #nodejs-packagejson-field-definitions 
14371169[ entry points ] : #package-entry-points 
14381170[ folders as modules ] : modules.md#folders-as-modules 
@@ -1446,3 +1178,4 @@ This field defines [subpath imports][] for the current package.
14461178[ supported package managers ] : corepack.md#supported-package-managers 
14471179[ the dual CommonJS/ES module packages section ] : #dual-commonjses-module-packages 
14481180[ the full specifier path ] : esm.md#mandatory-file-extensions 
1181+ [ the package examples repository ] : https://github.com/nodejs/package-examples 
0 commit comments