Prefer imports over require

JavaScript pattern

Converts require statements to ES6-style import.


Apply with the Grit CLI
grit apply es6_imports

Transform standard require statements

BEFORE
const defaultImport = require('../../shared/default').default;
const { something, another } = require('./lib');
const { value, original: renamed } = require('something');
const otherName = require('chai').ogName;
const assert = require('chai').assert;
const conf = require('chai').config;
const starImport = require('star');
AFTER
import defaultImport from '../../shared/default';
import { something, another } from './lib';
import { value, original as renamed } from 'something';
import { ogName as otherName } from 'chai';
import { assert } from 'chai';
import { config as conf } from 'chai';
import starImport from 'star';

Handle dotenv

BEFORE
require('dotenv').config({ path: '../.env' });

// Another example
require('dotenv').config();

function doStuff() {
  // hello world
}
AFTER
import * as dotenv from 'dotenv';
dotenv.config({ path: '../.env' });

// Another example
import * as dotenv from 'dotenv';
dotenv.config();

function doStuff() {
  // hello world
}

Handle Sentry

BEFORE
const Sentry = require('@sentry/node');

This appears to be correct based on open source examples.

AFTER
import * as Sentry from '@sentry/node';

Handle deep props

BEFORE
const assert = require('test-lib').assert,
  path = require('path'),
  hash = require('../hash'),
  {
    Some: {
      Deep: { Concerns },
    },
  } = require('@org/pkg'),
  { ancestorExport } = require('../../ancestor');

const defaultOptions = require('../../conf/default-cli-options');
const pkg = require('../../package.json');

const {
  Legacy: {
    ConfigOps,
    naming,
    CascadingConfigArrayFactory,
    IgnorePattern,
    getUsedExtractedConfigs,
    ModuleResolver,
  },
} = require('@org/pkg');

const proxyquire = require('proxyquire').noCallThru().noPreserveCache();
AFTER
import { assert } from 'test-lib';
import path from 'path';
import hash from '../hash';
import { Some } from '@org/pkg';
const {
  Deep: { Concerns },
} = Some;
import { ancestorExport } from '../../ancestor';

import defaultOptions from '../../conf/default-cli-options';
import pkg from '../../package.json';

import { Legacy } from '@org/pkg';
const {
  ConfigOps,
  naming,
  CascadingConfigArrayFactory,
  IgnorePattern,
  getUsedExtractedConfigs,
  ModuleResolver,
} = Legacy;

import __proxyquire from 'proxyquire';
const proxyquire = __proxyquire.noCallThru().noPreserveCache();

Dynamic imports

Require statements that are not at the root of the program are converted to dynamic imports, using await import().

BEFORE
const { something } = require('./lib');

async function doStuff() {
  const { another } = require('another');

  // Handle sentry correctly too
  const Sentry = require('@sentry/node');

  // Destructure
  const {
    somethingElse: { finalThing },
  } = require('another');
}
AFTER
import { something } from './lib';

async function doStuff() {
  const { another } = await import('another');

  // Handle sentry correctly too
  const Sentry = await import('@sentry/node');

  // Destructure
  const {
    somethingElse: { finalThing },
  } = await import('another');
}

Inline require usage

Require statements that are used without being assigned to a variable are ignored.

JS
const input = await fs.readFile(require('path').resolve(__dirname, 'test.txt'), 'utf8');

Function usage

Requires inside a code block should also be ignored.

JS
const command = initUtil({
  async run() {
    var proc = require('child_process').spawn('ls', ['-l']);
  },
});

Ignore dynamically generated requires

Template literals and other computed requires cannot use import.

JS
const mybrand = 'grit';
const { something } = require(`./${mybrand}/lib`);