Skip to content
This repository was archived by the owner on Sep 2, 2023. It is now read-only.
This repository was archived by the owner on Sep 2, 2023. It is now read-only.

Proposal: --default-type #335

@GeoffreyBooth

Description

@GeoffreyBooth

This is a proposal to provide a way to change Node’s default module system from CommonJS to ESM (#318).

There are two places where the module system can be specified, with a lack of specificity defaulting to CommonJS:

  • package.json "type" field, where the lack of the field is interpreted as "commonjs".

  • --input-type, where the lack of the flag is interpreted as commonjs.

We propose a new flag --default-type, to control the default values of package.json "type" and --input-type:

  • --default-type=commonjs is the same as the current behavior, where the lack of the flag or field is interpreted as CommonJS.

  • --default-type=module would cause the lack of the flag or field to be interpreted as ESM.

The expectation is that users who prefer ESM to be their default would set this flag in their NODE_OPTIONS, e.g. NODE_OPTIONS=--default-type=module.

With the flag set to module, some potential use cases are:

  • Commands like node --eval 'import { version } from "process"; console.log(version)' could be run without needing --input-type=module.

  • Extensionless files to be run like shell scripts could use ESM syntax, without needing to be symlinks to .mjs files or use other workarounds.

Under --default-type=module, most typical projects with dozens of CommonJS dependencies would break. Most, if not all, of those dependencies would lack "type": "commonjs" in their package.json files, at least for now while public awareness of the new field grows. One potential solution is a script that users could run to add the field for any dependencies that lack it:

#!/usr/bin/env node
const { argv, exit } = require('process');
const { basename, resolve } = require('path');
const { readdir, open } = require('fs').promises;

if (argv.length !== 3 || argv[2] === '-h' || argv[2] === '--help') {
  const scriptCommand = basename(argv[1]);
  console.log(`This script searches a folder and its subfolders for package.json files,
adding "type": "commonjs" to any such files that lack a "type" field.
Usage:   ${scriptCommand} folder
Example: ${scriptCommand} ./node_modules`);
  exit(1);
} else {
  walk(resolve(argv[2])).catch(console.error);
}

async function walk(folder) {
  const entries = await readdir(folder, {withFileTypes: true});
  await Promise.all(entries.map(async (entry) => {
    if (entry.isDirectory()) {
      return walk(resolve(folder, entry.name));
    } else if (entry.name === 'package.json') {
      const resolvedFilePath = resolve(folder, entry.name);
      let handle;
      try {
        handle = await open(resolvedFilePath, 'r+');
        const manifest = JSON.parse(await handle.readFile('utf-8'));
        if (manifest.type === undefined) {
          manifest.type = 'commonjs';
          await handle.writeFile(JSON.stringify(manifest, null, 2));
        }
      } catch (exception) {
        console.error(`Error updating ${resolvedFilePath}:`, exception);
      } finally {
        if (handle) {
          await handle.close();
        }
      }
    }
  }));
}

We expect that package managers might soon begin to add "type": "commonjs" automatically for dependencies that lack a "type" field. If they don’t, and no other userland solution finds broad acceptance, another solution is for Node to add a third option to --default-type, namely --default-type=module-except-dependencies. This would behave the same as --default-type=module except that it wouldn’t apply to folders under a node_modules folder. We would prefer not to need to implement this, however, as a “pure” solution where package managers or userland utilities bridge the gap would let Node provide a more ideal API. This option can always be added later if it turns out to be necessary.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions