Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 12 additions & 30 deletions guide/02-single-file-package/classic-commonjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,15 @@ This is the most traditional way of creating a package in Node.js, using the Com

In this example, the `my-logger` package consists of a single file, logger.js, that implements and exports the `Logger` class.

```js
// logger.js
class Logger {
// ... Logger implementation
};
exports.Logger = Logger;
```
`logger.js`:

[import:'logger_start,logger_end'](node_modules/my-logger/logger.js)

The `package.json` file for this package looks like this:

```json
{
"name": "my-logger",
"version": "1.0.0",
"type": "commonjs",
"exports": {
".": "./logger.js",
"./package.json": "./package.json"
}
}
```
`package.json`:

[import](node_modules/my-logger/package.json)

So the package structure is as follows:

Expand All @@ -47,21 +35,15 @@ In addition, we have included an export for `./package.json` in the `"exports"`

When users load this package, they can do so like this:

```js
// app.cjs
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');
```
`app.cjs`:

[import:'doc'](app.cjs)

Or, a ESM consumer can load this package like this:

```js
// app.mjs
import { Logger } from 'my-logger';
const logger = new Logger('debug');
logger.error('This is an error message');
```
`app.mjs`:

[import](app.mjs)

Usually, a published package is placed in a `node_modules` directory of a project. When you use `require('my-logger')`, Node.js will start looking for a directory named `my-logger` in the nearest `node_modules` directory up until it reaches the root of the filesystem. You can refer to the [Node.js documentation](https://nodejs.org/api/modules.html#all-together) for more details about the module resolution algorithm.

Expand Down
2 changes: 2 additions & 0 deletions guide/02-single-file-package/classic-commonjs/app.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

//! [doc]
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');
//! [doc]

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 12 additions & 29 deletions guide/02-single-file-package/simple-esm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,15 @@ This is the more modern way of creating a package in Node.js, using the ESM syst

In this example, we have a package named `my-logger` that exports a `Logger` class. The package consists of a single file, `logger.js`, which contains the implementation of the `Logger` class.

```js
// logger.js
export class Logger {
// ... Logger implementation
};
```
`logger.js`:

[import:'logger_start,logger_end'](node_modules/my-logger/logger.js)

The `package.json` file for this package looks like this:

```json
{
"name": "my-logger",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./logger.js",
"./package.json": "./package.json"
}
}
```
`package.json`:

[import](node_modules/my-logger/package.json)

So the package structure is as follows:

Expand All @@ -46,21 +35,15 @@ In addition, we have included an export for `./package.json` in the `"exports"`

When users load this package, they can do so like this:

```js
// app.mjs
import { Logger } from 'my-logger';
const logger = new Logger('debug');
logger.error('This is an error message');
```
`app.mjs`:

[import](app.mjs)

Or, from Node.js 20 and above, CommonJS consumers can also load a ESM package like this:

```js
// app.cjs
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');
```
`app.cjs`:

[import:'doc'](app.cjs)

Usually, a published package is placed in a `node_modules` directory of a project. When you use `import 'my-logger'`, Node.js will start looking for a directory named `my-logger` in the nearest `node_modules` directory up until it reaches the root of the filesystem. The module resolution algorithm for ESM is slightly different from the one used for loading CommonJS modules. You can refer to the [Node.js documentation](https://nodejs.org/api/esm.html#resolution-algorithm-specification) for more details.

Expand Down
2 changes: 2 additions & 0 deletions guide/02-single-file-package/simple-esm/app.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

//! [doc]
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');
//! [doc]

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 16 additions & 50 deletions guide/03-multi-file-package/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,23 @@ my-logger/
└── package.json
```

```js
// lib/utils.js
export const LEVELS = { /* ... log levels ... */ };
```
`lib/utils.js`:

```js
// src/logger.js
import { LEVELS } from '../lib/utils.js';
export class Logger {
// ... Logger implementation
};
```
[import:'abbrev,abbrev_end'](example/node_modules/my-logger/lib/utils.js)

```js
// index.js
export { Logger } from './src/logger.js';
```
`src/logger.js`:

[import:'logger_start,logger_end'](example/node_modules/my-logger/src/logger.js)

`index.js`:

[import](example/node_modules/my-logger/index.js)

The `package.json` file for this package would typically look like this:

```json
{
"name": "my-logger",
"version": "1.0.0",
"type": "module",
"scripts": {
"test": "node --test"
},
"exports": {
".": "./index.js",
"./package.json": "./package.json"
},
"files": [
"lib/",
"src/",
"index.js"
]
}
```
`package.json`:

[import](example/node_modules/my-logger/package.json)

When this package is published, because `test` is not listed in `files`, this directory will be excluded in the published package. Here we use `npm pack` to verify it (though this convention is generally respected by most package managers):

Expand All @@ -74,26 +52,14 @@ package/package.json

In addition, when users load this package, they can only access `index.js` and `package.json`, but not the internal files. This allows maintainers to change the internal structure of the package without breaking users who may come to assume the internal files are part of the public API.

```js
// app.mjs
import { Logger } from 'my-logger';
const logger = new Logger('debug');
logger.error('This is an error message');
`app.mjs`:

// This will throw ERR_PACKAGE_PATH_NOT_EXPORTED
await import('my-logger/lib/utils.js');
```
[import](example/app.mjs)

This works similarly for CommonJS consumers (since the package is ESM, they will need to use Node.js 20 or above to load it from `require()`):

```js
// app.cjs
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');
`app.cjs`:

// This will throw ERR_PACKAGE_PATH_NOT_EXPORTED
require('my-logger/lib/utils.js');
```
[import:'doc'](example/app.cjs)

You can find an example of this package on [GitHub](https://github.com/nodejs/package-examples/tree/main/guide/03-multi-file-package/example).
9 changes: 9 additions & 0 deletions guide/03-multi-file-package/example/app.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
'use strict';

//! [doc]
const { Logger } = require('my-logger');
const logger = new Logger('debug');
logger.error('This is an error message');

try {
// This will throw ERR_PACKAGE_PATH_NOT_EXPORTED
require('my-logger/lib/utils.js');
} catch (e) {
console.error(e.code, e.message);
}
//! [doc]
7 changes: 7 additions & 0 deletions guide/03-multi-file-package/example/app.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { Logger } from 'my-logger';
const logger = new Logger('debug');
logger.error('This is an error message');

try {
// This will throw ERR_PACKAGE_PATH_NOT_EXPORTED
await import('my-logger/lib/utils.js');
} catch (e) {
console.error(e.code, e.message);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading