File extension transformer
Getting Started
Installation
Usage
Built With
Contributing
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) declarationsimportdeclarationsrequirestatements- sourcemaps
In addition to file extension transformations:
- Set custom file extension search patterns
- Use functions to dynamically generate extensions
- Pass custom Babel transform options
yarn add -D @flex-development/trext # or npm i -D @flex-development/trextRunning 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)))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.
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)))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)))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)))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)))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)))- @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