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 file extensions, import
statements, call expressions, and even source maps!
Heavily inspired by convert-extension
, trext
intends to provide a more
configurable user experience. In addition to passing custom
Babel transform options, maintainers can also specify custom
file extension search patterns and use
functions to dynamically generate extensions.
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 '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)))
trext
implements a custom Babel plugin to update 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 '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 type { NodePath } from '@babel/traverse'
import type { CallExpression, ImportDeclaration } from '@babel/types'
import { isNode } from '@babel/types'
import type {
FileExtension,
TrextMatch,
TrextOptions
} from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from '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 NodePath<CallExpression | ImportDeclaration>
if (nodePath.type === 'CallExpression') {
//
}
return '.mjs'
}
// Check is 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 '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 series of modules from a
single index.*
file. Directory index syntax allows developers to import
and
require
those entries without including /index.*
in the module specifier:
/**
* @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'
Specifiers './interfaces'
and './types'
use directory index syntax and
will be ignored when encountered by Trextel
.
By default, index lookups are performed in the process.cwd()/src
directory.
Set src
to change the lookup location:
import type { TrextOptions } from '@flex-development/trext'
import { trext } from '@flex-development/trext'
import { inspect } from '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)))
- @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