From 07e6bda56ed76696d295e8878b5f0f87f5441d21 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Sun, 29 May 2022 14:34:05 +0200 Subject: [PATCH] :sparkles: add inputType option to force ESM module extension --- README.md | 12 ++++++++++++ src/builder.test.ts | 21 ++++++++++++--------- src/builder.ts | 20 +++++++++++--------- src/builders/fileSystem.test.ts | 22 +++++++++++++--------- src/builders/fileSystem.ts | 8 ++++++-- src/builders/flat.test.ts | 15 ++++++++++----- src/builders/flat.ts | 8 ++++++-- src/index.test.ts | 6 ++++-- src/index.ts | 3 ++- src/options/options.ts | 17 +++++++++++++++-- src/tasks/BuildBarrel.ts | 10 +++++++--- 11 files changed, 98 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 99168848..7296a7a0 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,18 @@ Use 'single quotes' in the generated barrel files instead of the default "double Omit semicolons from the end of lines in the generated barrel files. +### `-m` or `--esm` + +Enable ESM mode. Add `.js` extension to imported files. + +```typescript +export * from "./barrel.js"; +export * from "./index.js"; +export * from "./directory2/script.js"; +export * from "./directory2/directory4/deeplyNested.js"; +export * from "./directory3/program.js"; +``` + ### `-v` or `--version` Display the barrelsby version number. diff --git a/src/builder.test.ts b/src/builder.test.ts index 4a0073bc..44e0ce8c 100644 --- a/src/builder.test.ts +++ b/src/builder.test.ts @@ -2,20 +2,20 @@ import fs from 'fs'; import MockFs from 'mock-fs'; import Sinon from 'sinon'; -import { build, buildImportPath, getBasename } from './builder'; +import {build, buildImportPath, getBasename} from './builder'; import * as FileSystem from './builders/fileSystem'; import * as Flat from './builders/flat'; import * as Header from './builders/header'; import * as Modules from './modules'; -import { StructureOption } from './options/options'; +import {InputTypeOption, StructureOption} from './options/options'; import * as TestUtilities from './testUtilities'; -import { Directory } from './interfaces/directory.interface'; -import { FileTreeLocation } from './interfaces/location.interface'; -import { Signale } from 'signale'; -import { BaseUrl } from './options/baseUrl'; -import { Logger } from './options/logger'; -import { SemicolonCharacter } from './options/noSemicolon'; -import { QuoteCharacter } from './options/quoteCharacter'; +import {Directory} from './interfaces/directory.interface'; +import {FileTreeLocation} from './interfaces/location.interface'; +import {Signale} from 'signale'; +import {BaseUrl} from './options/baseUrl'; +import {Logger} from './options/logger'; +import {SemicolonCharacter} from './options/noSemicolon'; +import {QuoteCharacter} from './options/quoteCharacter'; import * as BuildBarrelModule from './tasks/BuildBarrel'; // Gets a location from a list by name. @@ -35,6 +35,7 @@ describe('builder/builder module has a', () => { barrelType: StructureOption; quoteCharacter: QuoteCharacter; semicolonCharacter: SemicolonCharacter; + inputType: InputTypeOption; barrelName: string; logger: Logger; // Gets a location from a list by name. @@ -63,6 +64,7 @@ describe('builder/builder module has a', () => { local: false, include: [], exclude: [], + inputType: InputTypeOption.COMMONJS, }); }; beforeEach(() => { @@ -150,6 +152,7 @@ describe('builder/builder module has a', () => { local: false, include: [], exclude: [], + inputType:InputTypeOption.MODULE, }); }; beforeEach(() => { diff --git a/src/builder.ts b/src/builder.ts index 4cd785eb..a987672f 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -1,13 +1,13 @@ import path from 'path'; -import { BaseUrl } from './options/baseUrl'; -import { Logger } from './options/logger'; -import { SemicolonCharacter } from './options/noSemicolon'; -import { StructureOption } from './options/options'; -import { QuoteCharacter } from './options/quoteCharacter'; -import { convertPathSeparator, thisDirectory } from './utilities'; -import { buildBarrel } from './tasks/BuildBarrel'; -import { Directory } from './interfaces/directory.interface'; -import { FileTreeLocation } from './interfaces/location.interface'; +import {BaseUrl} from './options/baseUrl'; +import {Logger} from './options/logger'; +import {SemicolonCharacter} from './options/noSemicolon'; +import {InputTypeOption, StructureOption} from './options/options'; +import {QuoteCharacter} from './options/quoteCharacter'; +import {convertPathSeparator, thisDirectory} from './utilities'; +import {buildBarrel} from './tasks/BuildBarrel'; +import {Directory} from './interfaces/directory.interface'; +import {FileTreeLocation} from './interfaces/location.interface'; export const build = (params: { destinations: Directory[]; @@ -21,6 +21,7 @@ export const build = (params: { local: boolean; include: string[]; exclude: string[]; + inputType: InputTypeOption | undefined; }): void => { try { // Build the barrels. @@ -37,6 +38,7 @@ export const build = (params: { local: params.local, include: params.include, exclude: params.exclude, + inputType: params.inputType ?? InputTypeOption.COMMONJS, }) ); } catch (e) { diff --git a/src/builders/fileSystem.test.ts b/src/builders/fileSystem.test.ts index 2cc4b5f6..8bd6e791 100644 --- a/src/builders/fileSystem.test.ts +++ b/src/builders/fileSystem.test.ts @@ -1,6 +1,7 @@ import * as TestUtilities from '../testUtilities'; import * as FileSystem from './fileSystem'; -import { Signale } from 'signale'; +import {Signale} from 'signale'; +import {InputTypeOption} from "../options/options"; describe('builder/fileSystem module has a', () => { describe('buildFileSystemBarrel function that', () => { @@ -15,7 +16,8 @@ describe('builder/fileSystem module has a', () => { '"', ';', logger, - undefined + undefined, + InputTypeOption.COMMONJS ); }); it('should produce the correct output', () => { @@ -57,17 +59,18 @@ export {indexts as index}; "'", ';', logger, - undefined + undefined, + InputTypeOption.MODULE ); }); it('should produce the correct output', () => { TestUtilities.assertMultiLine( output, - `import * as barrelts from './barrel'; -import * as directory2directory4deeplyNestedts from './directory2/directory4/deeplyNested'; -import * as directory2scriptts from './directory2/script'; -import * as directory3programts from './directory3/program'; -import * as indexts from './index'; + `import * as barrelts from './barrel.js'; +import * as directory2directory4deeplyNestedts from './directory2/directory4/deeplyNested.js'; +import * as directory2scriptts from './directory2/script.js'; +import * as directory3programts from './directory3/program.js'; +import * as indexts from './index.js'; export {barrelts as barrel}; export const directory2 = { directory4: { @@ -98,7 +101,8 @@ export {indexts as index}; '"', '', logger, - undefined + undefined, + InputTypeOption.COMMONJS ); }); it('should produce the correct output', () => { diff --git a/src/builders/fileSystem.ts b/src/builders/fileSystem.ts index ffdbb8cd..7464dfba 100644 --- a/src/builders/fileSystem.ts +++ b/src/builders/fileSystem.ts @@ -8,6 +8,7 @@ import { QuoteCharacter } from '../options/quoteCharacter'; import { indentation, nonAlphaNumeric } from '../utilities'; import { Directory } from '../interfaces/directory.interface'; import { FileTreeLocation } from '../interfaces/location.interface'; +import {InputTypeOption} from "../options/options"; function stringify(structure: ExportStructure, previousIndentation: string): string { const nextIndentation = previousIndentation + indentation; @@ -62,10 +63,13 @@ export function buildFileSystemBarrel( quoteCharacter: QuoteCharacter, semicolonCharacter: SemicolonCharacter, _: Logger, // Not used - baseUrl: BaseUrl + baseUrl: BaseUrl, + inputType: InputTypeOption ): string { const structure: ExportStructure = {}; let content = ''; + const ext = `${inputType === InputTypeOption.MODULE ? '.js' : ''}`; + modules .map( (module: FileTreeLocation): Import => ({ @@ -79,7 +83,7 @@ export function buildFileSystemBarrel( const directoryPath = path.dirname(relativePath); const parts = directoryPath.split(path.sep); const alias = relativePath.replace(nonAlphaNumeric, ''); - content += `import * as ${alias} from ${quoteCharacter}${imported.path}${quoteCharacter}${semicolonCharacter} + content += `import * as ${alias} from ${quoteCharacter}${imported.path}${ext}${quoteCharacter}${semicolonCharacter} `; const fileName = path.basename(imported.module.name, '.ts'); buildStructureSubsection(structure, parts, fileName, alias); diff --git a/src/builders/flat.test.ts b/src/builders/flat.test.ts index fe4164bc..8a42190b 100644 --- a/src/builders/flat.test.ts +++ b/src/builders/flat.test.ts @@ -2,7 +2,8 @@ import Sinon from 'sinon'; import * as TestUtilities from '../testUtilities'; import * as Flat from './flat'; -import { Signale } from 'signale'; +import {Signale} from 'signale'; +import {InputTypeOption} from "../options/options"; describe('builder/flat module has a', () => { describe('buildFlatBarrel function that', () => { @@ -22,7 +23,8 @@ describe('builder/flat module has a', () => { ';', signale, undefined, - false + false, + InputTypeOption.MODULE ); }); afterEach(() => { @@ -73,7 +75,8 @@ export * from "./directory3/program"; ';', signale, undefined, - false + false, + InputTypeOption.COMMONJS ); }); afterEach(() => { @@ -124,7 +127,8 @@ export * from './directory3/program'; '', signale, undefined, - false + false, + InputTypeOption.COMMONJS ); }); afterEach(() => { @@ -170,7 +174,8 @@ export * from "./directory3/program" ';', signale, undefined, - true + true, + InputTypeOption.COMMONJS ); }); afterEach(() => { diff --git a/src/builders/flat.ts b/src/builders/flat.ts index 2fdd8c44..fe04f849 100644 --- a/src/builders/flat.ts +++ b/src/builders/flat.ts @@ -5,6 +5,7 @@ import { SemicolonCharacter } from '../options/noSemicolon'; import { QuoteCharacter } from '../options/quoteCharacter'; import { Directory } from '../interfaces/directory.interface'; import { FileTreeLocation } from '../interfaces/location.interface'; +import { InputTypeOption } from "../options/options"; export function buildFlatBarrel( directory: Directory, @@ -13,14 +14,17 @@ export function buildFlatBarrel( semicolonCharacter: SemicolonCharacter, logger: Logger, baseUrl: BaseUrl, - exportDefault: boolean + exportDefault: boolean, + inputType: InputTypeOption ): string { + const ext = `${inputType === InputTypeOption.MODULE ? '.js' : ''}`; + return modules.reduce((previous: string, current: FileTreeLocation) => { const importPath = buildImportPath(directory, current, baseUrl); logger.debug(`Including path ${importPath}`); if (exportDefault) { const filename = getBasename(current.path); - previous += `export { default as ${filename} } from ${quoteCharacter}${importPath}${quoteCharacter}${semicolonCharacter} + previous += `export { default as ${filename} } from ${quoteCharacter}${importPath}${ext}${quoteCharacter}${semicolonCharacter} `; } return (previous += `export * from ${quoteCharacter}${importPath}${quoteCharacter}${semicolonCharacter} diff --git a/src/index.test.ts b/src/index.test.ts index a916d520..9ebe16f4 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -10,6 +10,7 @@ import * as RootPath from './options/rootPath'; import * as Purge from './purge'; import Sinon from 'sinon'; import * as Builder from './builder'; +import {StructureOption} from "./options/options"; describe('main module', () => { let spySandbox: Sinon.SinonSandbox; @@ -32,8 +33,8 @@ describe('main module', () => { name: 'inputBarrelName', noSemicolon: true, singleQuotes: true, - structure: 'flat', - verbose: true, + structure: StructureOption.FLAT, + verbose: true }; const builtTree: any = { mock: 'built tree' }; @@ -82,6 +83,7 @@ describe('main module', () => { barrelName, logger: signale, baseUrl, + inputType: args.inputType, exportDefault: args.exportDefault, structure: args.structure, local: args.local, diff --git a/src/index.ts b/src/index.ts index e637feac..5bbb978d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,7 @@ import { Directory } from './interfaces/directory.interface'; // TODO: Document how users can call this from their own code without using the CLI. // TODO: We might need to do some parameter validation for that. -export function Barrelsby(args: Arguments) { +export function Barrelsby(args: Partial) { // Get the launch options/arguments. const logger = getLogger({ isVerbose: args.verbose ?? false }); const barrelName = getBarrelName(args.name ?? '', logger); @@ -55,6 +55,7 @@ export function Barrelsby(args: Arguments) { destinations, quoteCharacter, semicolonCharacter, + inputType: args.inputType, barrelName, logger, baseUrl, diff --git a/src/options/options.ts b/src/options/options.ts index 11e2d310..a1407300 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -7,6 +7,11 @@ export enum StructureOption { FILESYSTEM = 'filesystem', } +export enum InputTypeOption { + COMMONJS = 'commonjs', + MODULE = 'module', +} + // Options provided by yargs. export interface Arguments { [x: string]: unknown; @@ -24,6 +29,7 @@ export interface Arguments { noSemicolon?: boolean; singleQuotes?: boolean; structure?: StructureOption; + inputType?: InputTypeOption; version?: boolean; verbose?: boolean; } @@ -94,8 +100,8 @@ export function getOptionsConfig(configParser: any): { type: 'string', alias: 'structure', description: 'The mode for structuring barrel file exports', - choices: ['flat', 'filesystem'], - default: 'flat', + choices: [StructureOption.FLAT, StructureOption.FILESYSTEM], + default: StructureOption.FLAT, }, q: { type: 'boolean', @@ -115,5 +121,12 @@ export function getOptionsConfig(configParser: any): { description: 'Display additional logging information', default: false, }, + t: { + type: 'string', + alias: 'inputType', + description: 'Force Barrelsby to use commonjs or ES module. By default the value is commonjs', + choices: [InputTypeOption.COMMONJS, InputTypeOption.MODULE], + default: InputTypeOption.COMMONJS, + }, }; } diff --git a/src/tasks/BuildBarrel.ts b/src/tasks/BuildBarrel.ts index 594c431c..b9889a13 100644 --- a/src/tasks/BuildBarrel.ts +++ b/src/tasks/BuildBarrel.ts @@ -10,7 +10,7 @@ import fs from 'fs'; import { Directory } from '../interfaces/directory.interface'; import { buildFileSystemBarrel } from '../builders/fileSystem'; import { buildFlatBarrel } from '../builders/flat'; -import { StructureOption } from '../options/options'; +import {InputTypeOption, StructureOption} from '../options/options'; export const buildBarrel = ({ directory, @@ -24,9 +24,11 @@ export const buildBarrel = ({ local, include, exclude, + inputType }: { directory: Directory; barrelType: StructureOption; + inputType: InputTypeOption; quoteCharacter: QuoteCharacter; semicolonCharacter: SemicolonCharacter; barrelName: string; @@ -46,7 +48,8 @@ export const buildBarrel = ({ quoteCharacter, semicolonCharacter, logger, - baseUrl + baseUrl, + inputType ); } else if (barrelType === StructureOption.FLAT) { content = buildFlatBarrel( @@ -56,7 +59,8 @@ export const buildBarrel = ({ semicolonCharacter, logger, baseUrl, - exportDefault + exportDefault, + inputType ); } else { throw new Error('No barrel type provided... this is likely a code error');