Skip to content

Optional configuration for multiple imports per line and strictly one import per line. #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"homepage": "https://github.com/TypeScript-Heroes/node-typescript-parser#readme",
"devDependencies": {
"@types/lodash-es": "^4.17.0",
"@types/jest": "^21.1.8",
"@types/mock-fs": "^3.6.30",
"@types/node": "^8.0.57",
Expand All @@ -45,7 +46,8 @@
"typedoc": "^0.9.0"
},
"dependencies": {
"lodash": "^4.17.4",
"lodash": "^4.17.5",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if you want to use lodash-es instead of lodash (which is fine by me, since I didn't knew it existed) you can remove the other one right? or are both needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are needed, I only had to add these because I was facing errors in running npm run develop. Right now lodash imports are untouched. At some point I would change those imports to use lodash-es. With that I tihnk we can eventually get treeshaking benefit.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright

"lodash-es": "^4.17.5",
"tslib": "^1.8.1",
"typescript": "^2.6.2"
}
Expand Down
22 changes: 22 additions & 0 deletions src/code-generators/TypescriptGenerationOptions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export enum MultiLineImportRule {
strictlyOneImportPerLine = 'strictlyOneImportPerLine',
oneImportPerLineOnlyAfterThreshold = 'oneImportPerLineOnlyAfterThreshold',
multipleImportsPerLine = 'multipleImportsPerLine',
}

/**
* Typescript generation options type. Contains all information needed to stringify some objects to typescript.
*
Expand Down Expand Up @@ -29,6 +35,14 @@ export interface TypescriptGenerationOptions {
*/
spaceBraces: boolean;

/**
* The wrapping methodology to be used for imports.
*
* @type {MultiLineImportRule}
* @memberof TypescriptGenerationOptions
*/
wrapMethod: MultiLineImportRule;

/**
* The threshold where an import is written as multiline.
*
Expand All @@ -52,4 +66,12 @@ export interface TypescriptGenerationOptions {
* @memberof TypescriptGenerationOptions
*/
tabSize: number;

/**
* Insert spaces instead of tabs (default: true)
*
* @type {boolean}
* @memberof TypescriptGenerationOptions
*/
insertSpaces: boolean;
}
74 changes: 52 additions & 22 deletions src/code-generators/typescript-generators/namedImport.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { NamedImport } from '../../imports/NamedImport';
import { SymbolSpecifier } from '../../SymbolSpecifier';
import { stringTemplate } from '../../utilities/StringTemplate';
import { TypescriptGenerationOptions } from '../TypescriptGenerationOptions';
import { TypescriptGenerationOptions, MultiLineImportRule } from '../TypescriptGenerationOptions';
import { generateSymbolSpecifier } from './symbolSpecifier';

const importTemplate = stringTemplate`import ${0} from ${1}`;
const oneLinerImportTemplate = stringTemplate`import ${0} from ${1}`;

const multiLineImport = stringTemplate`import ${3}{
const multiLineImportTemplate = stringTemplate`import ${3}{
${0}${1}
} from ${2}`;

const defaultAliasOnlyMultiLineImportTemplate = stringTemplate`import ${0}
from ${1}`;

/**
* Sort function for symbol specifiers. Does sort after the specifiers name (to lowercase).
*
Expand Down Expand Up @@ -45,34 +48,61 @@ export function generateNamedImport(
stringQuoteStyle,
spaceBraces,
tabSize,
wrapMethod,
multiLineWrapThreshold,
multiLineTrailingComma,
insertSpaces = true,
}: TypescriptGenerationOptions,
): string {
const space = spaceBraces ? ' ' : '';
const lib = `${stringQuoteStyle}${imp.libraryName}${stringQuoteStyle}${eol}`;

const specifiers = imp.specifiers.sort(specifierSort).map(o => generateSymbolSpecifier(o)).join(', ');
let importSpecifiers = `${space}${specifiers}${space}`;
if (importSpecifiers.trim().length === 0) {
importSpecifiers = ' ';
// const specifiers = imp.specifiers.sort(specifierSort).map(o => generateSymbolSpecifier(o)).join(', ');
const oneLinerImportStatement = oneLinerImportTemplate(getImportSpecifiers(imp, spaceBraces), lib);
if (oneLinerImportStatement.length <= multiLineWrapThreshold &&
(wrapMethod !== MultiLineImportRule.strictlyOneImportPerLine ||
imp.specifiers.length <= 1)) {
return oneLinerImportStatement;
}

const importString = importTemplate(
getImportSpecifiers(imp, spaceBraces),
lib,
);

if (importString.length > multiLineWrapThreshold) {
const spacings = Array(tabSize + 1).join(' ');
return multiLineImport(
imp.specifiers.sort(specifierSort).map(o => `${spacings}${generateSymbolSpecifier(o)}`).join(',\n'),
multiLineTrailingComma ? ',' : '',
`${stringQuoteStyle}${imp.libraryName}${stringQuoteStyle}${eol}`,
const defaultAliasOnly: boolean = imp.specifiers.length === 0;
if (defaultAliasOnly) {
return defaultAliasOnlyMultiLineImportTemplate(
imp.defaultAlias ? `${imp.defaultAlias}, ` : '',
`${stringQuoteStyle}${imp.libraryName}${stringQuoteStyle}${eol}`,
);
}
return importString;

const sortedImportSpecifiers: SymbolSpecifier[] = imp.specifiers.sort(specifierSort);
let importSpecifierStrings: string = '';
const indent = insertSpaces ? Array(tabSize + 1).join(' ') : '\t';
if (wrapMethod === MultiLineImportRule.strictlyOneImportPerLine ||
wrapMethod === MultiLineImportRule.oneImportPerLineOnlyAfterThreshold) {
importSpecifierStrings = sortedImportSpecifiers.map(o => `${indent}${generateSymbolSpecifier(o)}`).join(',\n');
} else if (wrapMethod === MultiLineImportRule.multipleImportsPerLine) {
importSpecifierStrings = sortedImportSpecifiers.reduce(
(acc, curr) => {
const symbolSpecifier: string = generateSymbolSpecifier(curr);
// const dist: number = acc.out.length - acc.lastWrapOffset + symbolSpecifier.length;
const importLines = acc.out.split('\n');
const lastImportLine = importLines[importLines.length - 1];
const dist: number = lastImportLine.length + `, `.length + symbolSpecifier.length;
const needsWrap: boolean = dist >= multiLineWrapThreshold;
return {
out: acc.out + (needsWrap ? `,\n${indent}` : (acc.out.length ? `, ` : `${indent}`)) +
symbolSpecifier,
lastWrapOffset: acc.lastWrapOffset + (needsWrap ? dist : 0),
};
},
{
out: '',
lastWrapOffset: 0,
},
).out;
}
return multiLineImportTemplate(
importSpecifierStrings,
multiLineTrailingComma ? ',' : '',
`${stringQuoteStyle}${imp.libraryName}${stringQuoteStyle}${eol}`,
imp.defaultAlias ? `${imp.defaultAlias}, ` : '',
);
}

function getImportSpecifiers(namedImport: NamedImport, spaceBraces: boolean): string {
Expand Down
50 changes: 47 additions & 3 deletions test/code-generators/TypescriptCodeGenerator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TypescriptCodeGenerator } from '../../src/code-generators/TypescriptCodeGenerator';
import { TypescriptGenerationOptions } from '../../src/code-generators/TypescriptGenerationOptions';
import { TypescriptGenerationOptions, MultiLineImportRule } from '../../src/code-generators/TypescriptGenerationOptions';
import { ClassDeclaration } from '../../src/declarations';
import { GetterDeclaration, SetterDeclaration } from '../../src/declarations/AccessorDeclaration';
import { DeclarationVisibility } from '../../src/declarations/DeclarationVisibility';
Expand Down Expand Up @@ -37,6 +37,9 @@ multiLineNamedImport.specifiers = [
new SymbolSpecifier('spec13'),
new SymbolSpecifier('spec14'),
new SymbolSpecifier('spec15'),
new SymbolSpecifier('spec16'),
new SymbolSpecifier('spec17'),
new SymbolSpecifier('spec18'),
];

const defaultImport = new NamedImport('defaultImport');
Expand All @@ -54,18 +57,42 @@ describe('TypescriptCodeGenerator', () => {
const defaultOptions: TypescriptGenerationOptions = {
eol: ';',
multiLineTrailingComma: true,
wrapMethod: MultiLineImportRule.oneImportPerLineOnlyAfterThreshold,
multiLineWrapThreshold: 125,
spaceBraces: true,
stringQuoteStyle: `'`,
tabSize: 4,
insertSpaces: true,
};
const impOptions: TypescriptGenerationOptions = {
const impOptions_oneImportPerLineOnlyAfterThreshold: TypescriptGenerationOptions = {
eol: ';',
multiLineTrailingComma: true,
wrapMethod: MultiLineImportRule.oneImportPerLineOnlyAfterThreshold,
multiLineWrapThreshold: 125,
spaceBraces: true,
stringQuoteStyle: `"`,
tabSize: 2,
insertSpaces: true,
};
const impOptions_strictlyOneImportPerLine: TypescriptGenerationOptions = {
eol: ';',
multiLineTrailingComma: true,
wrapMethod: MultiLineImportRule.strictlyOneImportPerLine,
multiLineWrapThreshold: 125,
spaceBraces: true,
stringQuoteStyle: `"`,
tabSize: 2,
insertSpaces: true,
};
const impOptions_multipleImportsPerLine: TypescriptGenerationOptions = {
eol: ';',
multiLineTrailingComma: true,
wrapMethod: MultiLineImportRule.multipleImportsPerLine,
multiLineWrapThreshold: 125,
spaceBraces: true,
stringQuoteStyle: `"`,
tabSize: 2,
insertSpaces: true,
};
const imports = [
new ExternalModuleImport('externalModuleLib', 'externalAlias'),
Expand Down Expand Up @@ -134,11 +161,28 @@ describe('TypescriptCodeGenerator', () => {
});

it(`should generate the correct code for ${imp.constructor.name} with double quote`, () => {
const generator = new TypescriptCodeGenerator(impOptions);
const generator = new TypescriptCodeGenerator(defaultOptions);

expect(generator.generate(imp)).toMatchSnapshot();
});

it(`should generate the correct code for ${imp.constructor.name} with double quote`, () => {
const generator = new TypescriptCodeGenerator(impOptions_oneImportPerLineOnlyAfterThreshold);

expect(generator.generate(imp)).toMatchSnapshot();
});

it(`should generate the correct code for ${imp.constructor.name} with double quote`, () => {
const generator = new TypescriptCodeGenerator(impOptions_strictlyOneImportPerLine);

expect(generator.generate(imp)).toMatchSnapshot();
});

it(`should generate multiple imports per line for ${imp.constructor.name} with single quote`, () => {
const generator = new TypescriptCodeGenerator(impOptions_multipleImportsPerLine);

expect(generator.generate(imp)).toMatchSnapshot();
});
}

it('should throw on non generatable element', () => {
Expand Down
Loading