Skip to content
This repository has been archived by the owner on Aug 16, 2022. It is now read-only.

Latest commit

 

History

History
352 lines (277 loc) · 9.82 KB

README.md

File metadata and controls

352 lines (277 loc) · 9.82 KB

⚙️ trext

File extension transformer

LICENSE Conventional Commits TypeScript tested with jest

Overview

Getting Started
Installation
Usage
Built With
Contributing

Getting Started

Interested in using .cjs and .mjs file extensions, but not in setting up another build workflow? Use trext to transform your project's file extensions. Heavily inspired by convert-extension.

Transform file extensions in:

  • export (default and named) declarations
  • import declarations
  • require statements
  • sourcemaps

In addition to file extension transformations:

Installation

yarn add -D @flex-development/trext # or npm i -D @flex-development/trext

Usage

Basic Usage

Running the example script below will convert any .js files and their relative imports to use .mjs extensions.

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Basic Usage
 * @module docs/examples/basic
 */

const TREXT_OPTIONS: TrextOptions<'js', 'mjs'> = {
  from: 'js',
  to: 'mjs'
}

trext('esm/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

Advanced Usage

Absolute Imports

By default, all absolute imports are ignored.

To transform extensions in absolute imports, set absolute to true or a RegExp object filter:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Absolute Imports
 * @module docs/examples/absolute
 */

const TREXT_OPTIONS: TrextOptions<'js', 'mjs'> = {
  absolute: /@flex-development/,
  from: 'js',
  to: 'mjs'
}

trext('esm/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

A regex filter is recommended unless all of your call expressions, exports, and/or imports use the same file extension.

Babel Transform

trext implements a custom Babel plugin to update export, import, and require statements. If enabled, source maps will also be updated. You can specify additional transform options using the babel property:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Babel Transform Options
 * @module docs/examples/babel
 */

const TREXT_OPTIONS: TrextOptions<'js', 'cjs'> = {
  babel: { comments: false, minified: true, sourceMaps: 'inline' as const },
  from: 'js',
  to: 'cjs'
}

trext('cjs/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

Dynamic File Extensions

trext uses the String.prototype.replace method to replace file extensions. The to option can also be specifed as a function to take full advantage of the method's capabilities. When using a function, however, note that the function will also be invoked within the context of Trextel, thus drastically changing the arguments passed to your function:

import { isNode } from '@babel/types'
import {
  FileExtension,
  trext,
  TrextMatch,
  TrextNodePath,
  TrextOptions
} from '@flex-development/trext'
import Trextel from '@flex-development/trext/plugins/esm/trextel.plugin'
import { inspect } from 'node:util'

/**
 * @file Examples - Dynamic File Extensions
 * @module docs/examples/dynamic
 */

const TREXT_OPTIONS: TrextOptions<'js', 'cjs' | 'mjs'> = {
  babel: { comments: false, minified: true, sourceMaps: 'inline' as const },
  from: 'js',
  to(match: TrextMatch, ...args: any[]): FileExtension<'cjs' | 'mjs'> {
    // Check if match is NodePath, args === []
    if (isNode((match as any).node)) {
      const nodePath = match as TrextNodePath
      const code: string | undefined = Trextel.getCode(nodePath)

      switch (nodePath.type) {
        case 'CallExpression':
          //
          break
        case 'ExportAllDeclaration':
          //
          break
        case 'ExportNamedDeclaration':
          //
          break
        case 'ImportDeclaration':
          //
          break
        default:
          break
      }

      return '.mjs'
    }

    // Check if match is RegExp object
    if (match.constructor.name === 'RegExp') {
      const regex = match as RegExp

      // do something!
    }

    // typeof match === 'string'
    const substring = match as string

    return '.cjs'
  }
}

trext('build/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

File Extension Search Patterns

If your naming convention includes dots (e.g: .interface.js), you'll want to specify a custom file extension search pattern:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Custom File Extension Search Pattern
 * @module docs/examples/pattern
 */

const TREXT_OPTIONS: TrextOptions<'js', 'mjs'> = {
  from: 'js',
  pattern: /.js$/,
  to: 'mjs'
}

trext('esm/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

Ignoring Directory Indexes

Directory entry points are a common way of exposing a group of modules from a single index.* file. Directory index (dirix) syntax allows developers to use partial import specifiers (or call expression arguments) to export, import, or require those modules without including /index.*:

/**
 * @file Package Entry Point
 * @module trext
 */

export { default as TREXT_DEFAULTS } from './config/defaults.config'
export * from './interfaces'
export { default as Trext, trext, trextFile } from './plugins/trext.plugin'
export * from './types'

Trextel searches for indexes in the process.cwd()/src directory. When mandatory extensions are disabled, partial specifiers and call expression arguments are ignored. Set src to change the directory index lookup location:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Ignoring Directory Indexes
 * @module docs/examples/src
 */

const TREXT_OPTIONS: TrextOptions<'js', 'cjs'> = {
  from: 'js',
  pattern: /.js$/,
  src: 'lib',
  to: 'cjs'
}

trext('cjs/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

Mandatory File Extensions

trext forces all specifiers (and call expression arguments) to be fully specified. Set mandatory to false to disable transformations for all TrextNode types:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Disabling Mandatory File Extensions
 * @module docs/examples/mandatory
 */

const TREXT_OPTIONS: TrextOptions<'js', 'mjs'> = {
  from: 'js',
  mandatory: false,
  pattern: /.js$/,
  to: 'mjs'
}

trext('esm/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

You can also disable transformations by TrextNode type:

import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from 'node:util'

/**
 * @file Examples - Disabling Mandatory File Extensions (By Node)
 * @module docs/examples/mandatory-by-node
 */

const TREXT_OPTIONS: TrextOptions<'js', 'mjs'> = {
  from: 'js',
  mandatory: {
    call: false,
    exportAll: true,
    exportNamed: false,
    import: true
  },
  pattern: /.js$/,
  to: 'mjs'
}

trext('mjs/', TREXT_OPTIONS)
  .then(results => console.info(inspect(results, false, null)))
  .catch(error => console.error(inspect(error, false, null)))

Built With

  • @babel/core - Babel compiler core
  • @babel/traverse - Traverse and update nodes
  • glob - Match file paths using glob patterns
  • mkdirp - Node.js implementation of mkdir -p
  • path-type - Check if a path is a file, directory, or symlink