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) declarationsimport
declarationsrequire
statements- 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/trext
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)))
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