Skip to content

Commit 48e5811

Browse files
committed
doc: fix transpiler loader hooks documentation
The loader hooks examples have been broken for a while: 1. The nextLoad() hook cannot be used on a .coffee file that ends up going to the default load step without an explict format, which would cause a ERR_UNKNOWN_FILE_EXTENSION. Mention adding a package.json with a type field to work around it in the example. 2. Pass the context parameter to the nextLoad() invocation. 3. Correct the getPackageType() implementation which returns false instead of undefined in the absence of an explict format, which is not a valid type for format.
1 parent 5c83957 commit 48e5811

File tree

1 file changed

+35
-41
lines changed

1 file changed

+35
-41
lines changed

doc/api/module.md

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,9 +1362,7 @@ transpiler hooks should only be used for development and testing purposes.
13621362
```mjs
13631363
// coffeescript-hooks.mjs
13641364
import { readFile } from 'node:fs/promises';
1365-
import { dirname, extname, resolve as resolvePath } from 'node:path';
1366-
import { cwd } from 'node:process';
1367-
import { fileURLToPath, pathToFileURL } from 'node:url';
1365+
import { findPackageJSON } from 'node:module';
13681366
import coffeescript from 'coffeescript';
13691367

13701368
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
@@ -1391,7 +1389,7 @@ export async function load(url, context, nextLoad) {
13911389
}
13921390

13931391
// Let Node.js handle all other URLs.
1394-
return nextLoad(url);
1392+
return nextLoad(url, context);
13951393
}
13961394

13971395
async function getPackageType(url) {
@@ -1402,37 +1400,23 @@ async function getPackageType(url) {
14021400
// this simple truthy check for whether `url` contains a file extension will
14031401
// work for most projects but does not cover some edge-cases (such as
14041402
// extensionless files or a url ending in a trailing space)
1405-
const isFilePath = !!extname(url);
1406-
// If it is a file path, get the directory it's in
1407-
const dir = isFilePath ?
1408-
dirname(fileURLToPath(url)) :
1409-
url;
1410-
// Compose a file path to a package.json in the same directory,
1411-
// which may or may not exist
1412-
const packagePath = resolvePath(dir, 'package.json');
1413-
// Try to read the possibly nonexistent package.json
1414-
const type = await readFile(packagePath, { encoding: 'utf8' })
1415-
.then((filestring) => JSON.parse(filestring).type)
1416-
.catch((err) => {
1417-
if (err?.code !== 'ENOENT') console.error(err);
1418-
});
1419-
// If package.json existed and contained a `type` field with a value, voilà
1420-
if (type) return type;
1421-
// Otherwise, (if not at the root) continue checking the next directory up
1422-
// If at the root, stop and return false
1423-
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
1403+
const pJson = findPackageJSON(url);
1404+
if (pJson) {
1405+
try {
1406+
const file = await readFile(pJson, 'utf8');
1407+
return JSON.parse(file)?.type;
1408+
} catch {}
1409+
}
1410+
return undefined;
14241411
}
14251412
```
14261413
14271414
##### Synchronous version
14281415
14291416
```mjs
14301417
// coffeescript-sync-hooks.mjs
1431-
import { readFileSync } from 'node:fs/promises';
1432-
import { registerHooks } from 'node:module';
1433-
import { dirname, extname, resolve as resolvePath } from 'node:path';
1434-
import { cwd } from 'node:process';
1435-
import { fileURLToPath, pathToFileURL } from 'node:url';
1418+
import { readFileSync } from 'node:fs';
1419+
import { registerHooks, findPackageJSON } from 'node:module';
14361420
import coffeescript from 'coffeescript';
14371421

14381422
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
@@ -1451,23 +1435,18 @@ function load(url, context, nextLoad) {
14511435
};
14521436
}
14531437

1454-
return nextLoad(url);
1438+
return nextLoad(url, context);
14551439
}
14561440

14571441
function getPackageType(url) {
1458-
const isFilePath = !!extname(url);
1459-
const dir = isFilePath ? dirname(fileURLToPath(url)) : url;
1460-
const packagePath = resolvePath(dir, 'package.json');
1461-
1462-
let type;
1463-
try {
1464-
const filestring = readFileSync(packagePath, { encoding: 'utf8' });
1465-
type = JSON.parse(filestring).type;
1466-
} catch (err) {
1467-
if (err?.code !== 'ENOENT') console.error(err);
1442+
const pJson = findPackageJSON(url);
1443+
if (pJson) {
1444+
try {
1445+
const file = readFileSync(pJson, 'utf-8');
1446+
return JSON.parse(file)?.type;
1447+
} catch { }
14681448
}
1469-
if (type) return type;
1470-
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
1449+
return undefined;
14711450
}
14721451

14731452
registerHooks({ load });
@@ -1489,6 +1468,21 @@ console.log "Brought to you by Node.js version #{version}"
14891468
export scream = (str) -> str.toUpperCase()
14901469
```
14911470
1471+
For the sake of running the example, add a `package.json` file containing the
1472+
module type of the CoffeeScript files.
1473+
1474+
```json
1475+
{
1476+
"type": "module"
1477+
}
1478+
```
1479+
1480+
In real world loaders, `getPackageType()` must be able to return an `format`
1481+
known to Node.js even in the absence of an explicit type in a `package.json`,
1482+
or otherwise the loader hook would throw `ERR_UNKNOWN_FILE_EXTENSION` (if undefined)
1483+
or `ERR_UNKNOWN_MODULE_FORMAT` (if it's not a known format listed in the [load hook][]
1484+
documentation).
1485+
14921486
With the preceding hooks modules, running
14931487
`node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee`
14941488
or `node --import ./coffeescript-sync-hooks.mjs ./main.coffee`

0 commit comments

Comments
 (0)