diff --git a/.appveyor.yml b/.appveyor.yml index 971a7a5..738a489 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,11 +2,13 @@ version: "{build} - {branch}" skip_tags: true skip_branch_with_pr: true +matrix: + fast_finish: true + environment: matrix: + - nodejs_version: "9" - nodejs_version: "8" - - nodejs_version: "7" - - nodejs_version: "6" install: - ps: Install-Product node $env:nodejs_version @@ -14,5 +16,7 @@ install: test_script: - npm test + - npm install -g codecov + - codecov build: off diff --git a/.travis.yml b/.travis.yml index fbaef08..a3c0b91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,38 @@ -sudo: false language: node_js -cache: - directories: - - node_modules +stages: + - name: test + if: tag IS blank + - name: deploy + if: branch = master AND type != pull_request + +matrix: + fast_finish: true notifications: email: false -node_js: - - '8' - - '7' - - '6' - -before_script: - - npm prune - -after_success: - - npm install coveralls@^2.11.9 && cat ./coverage/lcov.info | coveralls - -after_script: - - npm install - - npm run build - - npm run semantic-release - -branches: - except: - - /^v\d+\.\d+\.\d+$/ +jobs: + include: + - stage: test + node_js: '9' + after_success: + - npm i -g codecov + - codecov + - stage: test + node_js: '8' + after_success: + - npm i -g codecov + - codecov + - stage: deploy + node_js: '9' + script: npm run typedoc + deploy: + provider: pages + skip_cleanup: true + github_token: $GH_TOKEN + local_dir: ./docs + - stage: deploy + node_js: '9' + before_script: npm run build + script: npm run semantic-release diff --git a/README.md b/README.md index 8cc9537..b809e59 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ a more or less human readable AST out of .js or .ts files. [![Build Status](https://travis-ci.org/buehler/node-typescript-parser.svg)](https://travis-ci.org/buehler/node-typescript-parser) [![Build Status Windows](https://ci.appveyor.com/api/projects/status/j06bqjc4tkdt7sej?svg=true)](https://ci.appveyor.com/project/buehler/node-typescript-parser) [![npm](https://img.shields.io/npm/v/typescript-parser.svg?maxAge=3600)](https://www.npmjs.com/package/typescript-parser) -[![Coverage status](https://img.shields.io/coveralls/buehler/node-typescript-parser.svg?maxAge=3600)](https://coveralls.io/github/buehler/node-typescript-parser) +[![codecov](https://codecov.io/gh/buehler/node-typescript-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/buehler/node-typescript-parser) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Greenkeeper badge](https://badges.greenkeeper.io/buehler/node-typescript-parser.svg)](https://greenkeeper.io/) +[![Gitter](https://img.shields.io/gitter/room/node-typescript-parser/Lobby.svg)](https://gitter.im/node-typescript-parser/Lobby) ## How to use @@ -25,7 +26,7 @@ const parser = new TypescriptParser(); const parsed = await parser.parseSource(/* typescript source code as string */); // or a filepath -const parsed = await parser.parseSource('/user/myfile.ts', 'workspace root'); +const parsed = await parser.parseFile('/user/myfile.ts', 'workspace root'); ``` You can also parse multiple files at ones. @@ -37,10 +38,10 @@ After the parsing is done, you'll get an index with resolved exports and declarations. Keep in mind, that the index'll only contain exported declarations. - + ## Changelog -The changelog is generated by [semantic release](https://github.com/semantic-release/semantic-release) and is located under the +The changelog is generated by [semantic release](https://github.com/semantic-release/semantic-release) and is located under the [release section](https://github.com/buehler/node-typescript-parser/releases). ## Licence diff --git a/jest.json b/jest.json index ba69a19..6cda8fa 100644 --- a/jest.json +++ b/jest.json @@ -1,6 +1,5 @@ { "collectCoverage": true, - "mapCoverage": true, "transform": { "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" }, diff --git a/package.json b/package.json index cd7e643..b7a988f 100644 --- a/package.json +++ b/package.json @@ -31,22 +31,24 @@ }, "homepage": "https://github.com/TypeScript-Heroes/node-typescript-parser#readme", "devDependencies": { - "@types/jest": "^21.1.0", + "@smartive/tslint-config": "^2.0.0", + "@types/jest": "^22.1.4", + "@types/lodash-es": "^4.17.0", "@types/mock-fs": "^3.6.30", - "@types/node": "^8.0.31", + "@types/node": "^9.4.6", "del-cli": "^1.1.0", - "jest": "^21.2.0", - "mock-fs": "^4.4.1", - "semantic-release": "^9.0.0", - "ts-jest": "^21.0.0", - "tslint": "^5.5.0", - "tslint-config-airbnb": "^5.2.1", - "tsutils": "^2.9.0", - "typedoc": "^0.8.0" + "jest": "^22.4.2", + "mock-fs": "^4.4.2", + "semantic-release": "^15.0.0", + "ts-jest": "^22.4.0", + "tslint": "^5.9.1", + "tsutils": "^2.22.0", + "typedoc": "^0.10.0" }, "dependencies": { - "lodash": "^4.17.4", - "tslib": "^1.7.1", - "typescript": "^2.5.3" + "lodash": "^4.17.5", + "lodash-es": "^4.17.5", + "tslib": "^1.9.0", + "typescript": "^2.7.2" } } diff --git a/src/TypescriptParser.ts b/src/TypescriptParser.ts index 05c4282..c671d13 100644 --- a/src/TypescriptParser.ts +++ b/src/TypescriptParser.ts @@ -151,46 +151,54 @@ export class TypescriptParser { * * @memberof TsResourceParser */ - private parse(resource: Resource, node: Node): void { - for (const child of node.getChildren()) { - switch (child.kind) { + private parse(rootResource: Resource, rootNode: Node): void { + let [resource, ...resourceQueue]: Resource[] = Array(rootNode.getChildren().length).fill(rootResource); + let [node, ...nodeQueue]: Node[] = [...rootNode.getChildren()]; + while (node) { + switch (node.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ImportEqualsDeclaration: - parseImport(resource, child); + parseImport(resource, node); break; case SyntaxKind.ExportDeclaration: case SyntaxKind.ExportAssignment: - parseExport(resource, child); + parseExport(resource, node); break; case SyntaxKind.EnumDeclaration: - parseEnum(resource, child); + parseEnum(resource, node); break; case SyntaxKind.TypeAliasDeclaration: - parseTypeAlias(resource, child); + parseTypeAlias(resource, node); break; case SyntaxKind.FunctionDeclaration: - parseFunction(resource, child); + parseFunction(resource, node); + [resource, ...resourceQueue] = resourceQueue; + [node, ...nodeQueue] = nodeQueue; continue; case SyntaxKind.VariableStatement: - parseVariable(resource, child); + parseVariable(resource, node); break; case SyntaxKind.InterfaceDeclaration: - parseInterface(resource, child); + parseInterface(resource, node); break; case SyntaxKind.ClassDeclaration: - parseClass(resource, child); + parseClass(resource, node); + [resource, ...resourceQueue] = resourceQueue; + [node, ...nodeQueue] = nodeQueue; continue; case SyntaxKind.Identifier: - parseIdentifier(resource, child); + parseIdentifier(resource, node); break; case SyntaxKind.ModuleDeclaration: - const newResource = parseModule(resource, child); - this.parse(newResource, child); + const newResource = parseModule(resource, node); + [resource, ...resourceQueue] = [...Array(node.getChildren().length).fill(newResource), ...resourceQueue]; + [node, ...nodeQueue] = [...node.getChildren(), ...nodeQueue]; continue; default: break; } - this.parse(resource, child); + [resource, ...resourceQueue] = [...Array(node.getChildren().length).fill(resource), ...resourceQueue]; + [node, ...nodeQueue] = [...node.getChildren(), ...nodeQueue]; } } } diff --git a/src/code-generators/TypescriptGenerationOptions.ts b/src/code-generators/TypescriptGenerationOptions.ts index c58db01..800268e 100644 --- a/src/code-generators/TypescriptGenerationOptions.ts +++ b/src/code-generators/TypescriptGenerationOptions.ts @@ -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. * @@ -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. * @@ -52,4 +66,12 @@ export interface TypescriptGenerationOptions { * @memberof TypescriptGenerationOptions */ tabSize: number; + + /** + * Insert spaces instead of tabs (default: true) + * + * @type {boolean} + * @memberof TypescriptGenerationOptions + */ + insertSpaces: boolean; } diff --git a/src/code-generators/typescript-generators/namedImport.ts b/src/code-generators/typescript-generators/namedImport.ts index ca9c8d8..2a664e3 100644 --- a/src/code-generators/typescript-generators/namedImport.ts +++ b/src/code-generators/typescript-generators/namedImport.ts @@ -1,15 +1,18 @@ import { NamedImport } from '../../imports/NamedImport'; import { SymbolSpecifier } from '../../SymbolSpecifier'; import { stringTemplate } from '../../utilities/StringTemplate'; -import { TypescriptGenerationOptions } from '../TypescriptGenerationOptions'; +import { MultiLineImportRule, TypescriptGenerationOptions } 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). * @@ -23,7 +26,8 @@ function specifierSort(i1: SymbolSpecifier, i2: SymbolSpecifier): number { if (strA < strB) { return -1; - } else if (strA > strB) { + } + if (strA > strB) { return 1; } return 0; @@ -44,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 { diff --git a/src/node-parser/class-parser.ts b/src/node-parser/class-parser.ts index 3235a80..01eae50 100644 --- a/src/node-parser/class-parser.ts +++ b/src/node-parser/class-parser.ts @@ -20,11 +20,11 @@ import { Resource } from '../resources/Resource'; import { isArrayBindingPattern, isConstructorDeclaration, + isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isObjectBindingPattern, isPropertyDeclaration, - isGetAccessorDeclaration, isSetAccessorDeclaration, } from '../type-guards/TypescriptGuards'; import { parseFunctionParts, parseMethodParams } from './function-parser'; diff --git a/src/node-parser/export-parser.ts b/src/node-parser/export-parser.ts index 9ec0b18..12494be 100644 --- a/src/node-parser/export-parser.ts +++ b/src/node-parser/export-parser.ts @@ -11,7 +11,7 @@ import { getDefaultResourceIdentifier } from './parse-utilities'; /** * Parses an export node into the declaration. - * + * * @export * @param {Resource} resource * @param {(ExportDeclaration | ExportAssignment)} node @@ -19,7 +19,7 @@ import { getDefaultResourceIdentifier } from './parse-utilities'; export function parseExport(resource: Resource, node: ExportDeclaration | ExportAssignment): void { if (isExportDeclaration(node)) { const tsExport = node as ExportDeclaration; - if (!isStringLiteral(tsExport.moduleSpecifier)) { + if (!isStringLiteral(tsExport.moduleSpecifier) && !tsExport.exportClause) { return; } if (tsExport.getText().indexOf('*') > -1) { @@ -30,7 +30,11 @@ export function parseExport(resource: Resource, node: ExportDeclaration | Export ); } else if (tsExport.exportClause && isNamedExports(tsExport.exportClause)) { const lib = tsExport.moduleSpecifier as StringLiteral; - const ex = new NamedExport(node.getStart(), node.getEnd(), lib.text); + const ex = new NamedExport( + node.getStart(), + node.getEnd(), + lib ? lib.text : getDefaultResourceIdentifier(resource), + ); ex.specifiers = tsExport.exportClause.elements.map( o => o.propertyName && o.name ? @@ -38,14 +42,26 @@ export function parseExport(resource: Resource, node: ExportDeclaration | Export new SymbolSpecifier(o.name.text), ); + for (const spec of ex.specifiers) { + if (resource.usages.indexOf(spec.alias || spec.specifier) === -1) { + resource.usages.push(spec.alias || spec.specifier); + } + } + resource.exports.push(ex); } } else { const literal = node.expression as Identifier; if (node.isExportEquals) { resource.exports.push(new AssignedExport(node.getStart(), node.getEnd(), literal.text, resource)); + if (resource.usages.indexOf(literal.text) === -1) { + resource.usages.push(literal.text); + } } else { const name = (literal && literal.text) ? literal.text : getDefaultResourceIdentifier(resource); + if (resource.usages.indexOf(name) === -1) { + resource.usages.push(name); + } resource.declarations.push(new DefaultDeclaration(name, resource)); } } diff --git a/src/resources/File.ts b/src/resources/File.ts index 65914b4..f4d1e34 100644 --- a/src/resources/File.ts +++ b/src/resources/File.ts @@ -11,7 +11,7 @@ import { Resource } from './Resource'; /** * TypeScript resource. Basically a file that is located somewhere. - * + * * @export * @class File * @implements {Resource} @@ -32,15 +32,16 @@ export class File implements Resource, Node { return this.usages .filter(usage => !this.declarations.some(o => o.name === usage) && - !this.resources.some(o => (o instanceof Module || o instanceof Namespace) && o.name === usage)) + !this.resources.some(o => (o instanceof Module || o instanceof Namespace) && o.name === usage), + ) .concat( - this.resources.reduce((all, cur) => all.concat(cur.nonLocalUsages), [] as string[]), + this.resources.reduce((all, cur) => all.concat(cur.nonLocalUsages), [] as string[]), ); } /** * Returns the parsed path of a resource. - * + * * @readonly * @type {ParsedPath} * @memberof File @@ -51,7 +52,7 @@ export class File implements Resource, Node { /** * Determines if a file is a workspace file or an external resource. - * + * * @readonly * @type {boolean} * @memberof File diff --git a/src/resources/Module.ts b/src/resources/Module.ts index 2f84dcd..7b2c115 100644 --- a/src/resources/Module.ts +++ b/src/resources/Module.ts @@ -7,7 +7,7 @@ import { Resource } from './Resource'; /** * TypeScript resource. Declaration of a typescript module (i.e. declare module "foobar"). - * + * * @export * @class Module * @implements {Resource} @@ -39,9 +39,9 @@ export class Module implements Resource, Node { /** * Function that calculates the alias name of a namespace. * Removes all underlines and dashes and camelcases the name. - * + * * @returns {string} - * + * * @memberof Module */ public getNamespaceAlias(): string { @@ -49,9 +49,8 @@ export class Module implements Resource, Node { (all, cur, idx) => { if (idx === 0) { return all + cur.toLowerCase(); - } else { - return all + cur.charAt(0).toUpperCase() + cur.substring(1).toLowerCase(); } + return all + cur.charAt(0).toUpperCase() + cur.substring(1).toLowerCase(); }, '', ); diff --git a/src/resources/Namespace.ts b/src/resources/Namespace.ts index d304e21..6061d60 100644 --- a/src/resources/Namespace.ts +++ b/src/resources/Namespace.ts @@ -7,7 +7,7 @@ import { Resource } from './Resource'; /** * TypeScript resource. Declaration of a typescript namespace (i.e. declare foobar). - * + * * @export * @class Namespace * @implements {Resource} @@ -39,9 +39,9 @@ export class Namespace implements Resource, Node { /** * Function that calculates the alias name of a namespace. * Removes all underlines and dashes and camelcases the name. - * + * * @returns {string} - * + * * @memberof Namespace */ public getNamespaceAlias(): string { @@ -49,9 +49,8 @@ export class Namespace implements Resource, Node { (all, cur, idx) => { if (idx === 0) { return all + cur.toLowerCase(); - } else { - return all + cur.charAt(0).toUpperCase() + cur.substring(1).toLowerCase(); } + return all + cur.charAt(0).toUpperCase() + cur.substring(1).toLowerCase(); }, '', ); diff --git a/test/SpecificUsageCases.spec.ts b/test/SpecificUsageCases.spec.ts new file mode 100644 index 0000000..736e5ad --- /dev/null +++ b/test/SpecificUsageCases.spec.ts @@ -0,0 +1,41 @@ +import { TypescriptParser } from '../src'; +import { getWorkspaceFile, rootPath } from './testUtilities'; + +describe('Specific usage cases', () => { + const parser = new TypescriptParser(); + + describe('i18next with and without destructure', () => { + + it('should contain i18next reference in not destructured way', async () => { + const file = getWorkspaceFile('specific-usage-cases/i18next-destructure/import-my-component.tsx'); + const parsed = await parser.parseFile(file, rootPath); + expect(parsed.usages).toMatchSnapshot(); + }); + + it('should contain t reference in destructured way', async () => { + const file = getWorkspaceFile('specific-usage-cases/i18next-destructure/destructure-my-component.tsx'); + const parsed = await parser.parseFile(file, rootPath); + expect(parsed.usages).toMatchSnapshot(); + }); + + }); + + describe('get usage for directly reexported elements', () => { + + it('should contain imported elements in usages', async () => { + const file = getWorkspaceFile('specific-usage-cases/reexport/reexport-import.ts'); + const parsed = await parser.parseFile(file, rootPath); + expect(parsed.exports).toMatchSnapshot(); + expect(parsed.usages).toMatchSnapshot(); + }); + + it('should contain imported default elements in usages', async () => { + const file = getWorkspaceFile('specific-usage-cases/reexport/reexport-default.ts'); + const parsed = await parser.parseFile(file, rootPath); + expect(parsed.exports).toMatchSnapshot(); + expect(parsed.usages).toMatchSnapshot(); + }); + + }); + +}); diff --git a/test/__snapshots__/SpecificUsageCases.spec.ts.snap b/test/__snapshots__/SpecificUsageCases.spec.ts.snap new file mode 100644 index 0000000..6d50e41 --- /dev/null +++ b/test/__snapshots__/SpecificUsageCases.spec.ts.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Specific usage cases get usage for directly reexported elements should contain imported default elements in usages 1`] = `Array []`; + +exports[`Specific usage cases get usage for directly reexported elements should contain imported default elements in usages 2`] = ` +Array [ + "Test", +] +`; + +exports[`Specific usage cases get usage for directly reexported elements should contain imported elements in usages 1`] = ` +Array [ + NamedExport { + "end": 184, + "from": "reexport-import", + "specifiers": Array [ + SymbolSpecifier { + "alias": undefined, + "specifier": "colors", + }, + SymbolSpecifier { + "alias": undefined, + "specifier": "helpers", + }, + SymbolSpecifier { + "alias": undefined, + "specifier": "theme", + }, + SymbolSpecifier { + "alias": undefined, + "specifier": "icons", + }, + ], + "start": 143, + }, +] +`; + +exports[`Specific usage cases get usage for directly reexported elements should contain imported elements in usages 2`] = ` +Array [ + "colors", + "helpers", + "theme", + "icons", +] +`; + +exports[`Specific usage cases i18next with and without destructure should contain i18next reference in not destructured way 1`] = ` +Array [ + "MyComponent", + "p", + "i18next", +] +`; + +exports[`Specific usage cases i18next with and without destructure should contain t reference in destructured way 1`] = ` +Array [ + "MyComponent", + "p", + "t", +] +`; diff --git a/test/__snapshots__/TypescriptParser.spec.ts.snap b/test/__snapshots__/TypescriptParser.spec.ts.snap index 562e86f..d32b813 100644 --- a/test/__snapshots__/TypescriptParser.spec.ts.snap +++ b/test/__snapshots__/TypescriptParser.spec.ts.snap @@ -742,7 +742,10 @@ DefaultDeclaration { "resources": Array [], "start": 0, "usages": Array [ + "Specifier", + "Alias", "Foo", + "DefaultExport", ], }, "start": undefined, @@ -799,7 +802,10 @@ AssignedExport { "resources": Array [], "start": 0, "usages": Array [ + "Specifier", + "Alias", "Foo", + "DefaultExport", ], }, "start": 92, @@ -1743,6 +1749,7 @@ Array [ "React", "State", "defaultState", + "1", "connect", "mapStateToProps", "mapDispatchToProps", @@ -2090,6 +2097,7 @@ Array [ "React", "State", "defaultState", + "1", "connect", "mapStateToProps", "mapDispatchToProps", @@ -2437,6 +2445,7 @@ Array [ "React", "State", "defaultState", + "inline", "connect", "mapStateToProps", "mapDispatchToProps", diff --git a/test/_workspace/specific-usage-cases/i18next-destructure/destructure-my-component.tsx b/test/_workspace/specific-usage-cases/i18next-destructure/destructure-my-component.tsx new file mode 100644 index 0000000..cffd37a --- /dev/null +++ b/test/_workspace/specific-usage-cases/i18next-destructure/destructure-my-component.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { t } from 'i18next'; + +export const MyComponent = () => ( +

{t('foo')}

+); diff --git a/test/_workspace/specific-usage-cases/i18next-destructure/import-my-component.tsx b/test/_workspace/specific-usage-cases/i18next-destructure/import-my-component.tsx new file mode 100644 index 0000000..ee5c639 --- /dev/null +++ b/test/_workspace/specific-usage-cases/i18next-destructure/import-my-component.tsx @@ -0,0 +1,4 @@ +import * as i18next from 'i18next'; +import * as React from 'react'; + +export const MyComponent = () =>

{i18next.t('foo')}

; diff --git a/test/_workspace/specific-usage-cases/reexport/reexport-default.ts b/test/_workspace/specific-usage-cases/reexport/reexport-default.ts new file mode 100644 index 0000000..103aee1 --- /dev/null +++ b/test/_workspace/specific-usage-cases/reexport/reexport-default.ts @@ -0,0 +1,3 @@ +import Test from './Test'; + +export default Test; diff --git a/test/_workspace/specific-usage-cases/reexport/reexport-import.ts b/test/_workspace/specific-usage-cases/reexport/reexport-import.ts new file mode 100644 index 0000000..2ba18c3 --- /dev/null +++ b/test/_workspace/specific-usage-cases/reexport/reexport-import.ts @@ -0,0 +1,6 @@ +import * as colors from './colors'; +import * as helpers from './helpers'; +import * as icons from './icons'; +import * as theme from './theme'; + +export { colors, helpers, theme, icons }; diff --git a/test/code-generators/TypescriptCodeGenerator.spec.ts b/test/code-generators/TypescriptCodeGenerator.spec.ts index 8bf5904..4f57319 100644 --- a/test/code-generators/TypescriptCodeGenerator.spec.ts +++ b/test/code-generators/TypescriptCodeGenerator.spec.ts @@ -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'; @@ -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'); @@ -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'), @@ -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', () => { diff --git a/test/code-generators/__snapshots__/TypescriptCodeGenerator.spec.ts.snap b/test/code-generators/__snapshots__/TypescriptCodeGenerator.spec.ts.snap index 30013e0..e9e69d5 100644 --- a/test/code-generators/__snapshots__/TypescriptCodeGenerator.spec.ts.snap +++ b/test/code-generators/__snapshots__/TypescriptCodeGenerator.spec.ts.snap @@ -1,8 +1,40 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`TypescriptCodeGenerator should generate multiple imports per line for ExternalModuleImport with single quote 1`] = `"import externalAlias = require(\\"externalModuleLib\\");"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 1`] = `"import { spec1, spec2 as alias2 } from \\"namedLib\\";"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 2`] = ` +"import { + spec1, spec10, spec11, spec12, spec13, spec14, spec15, spec16, spec17, spec18, spec2, spec3, spec4, spec5, spec6, spec7, + spec8, spec9, +} from \\"multiLineNamedLib\\";" +`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 3`] = `"import { } from \\"emptyImport\\";"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 4`] = `"import Default from \\"defaultImport\\";"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 5`] = `"import Default, { spec1, spec2 as alias2 } from \\"defaultWithNamedImport\\";"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamedImport with single quote 6`] = ` +"import Default, { + spec1, spec10, spec11, spec12, spec13, spec14, spec15, spec16, spec17, spec18, spec2, spec3, spec4, spec5, spec6, spec7, + spec8, spec9, +} from \\"defaultWithNamedMultilineImport\\";" +`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for NamespaceImport with single quote 1`] = `"import * as namespaceAlias from \\"namespaceLib\\";"`; + +exports[`TypescriptCodeGenerator should generate multiple imports per line for StringImport with single quote 1`] = `"import \\"stringLib\\";"`; + exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport 1`] = `"import externalAlias = require('externalModuleLib');"`; -exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport with double quote 1`] = `"import externalAlias = require(\\"externalModuleLib\\");"`; +exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport with double quote 1`] = `"import externalAlias = require('externalModuleLib');"`; + +exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport with double quote 2`] = `"import externalAlias = require(\\"externalModuleLib\\");"`; + +exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport with double quote 3`] = `"import externalAlias = require(\\"externalModuleLib\\");"`; exports[`TypescriptCodeGenerator should generate the correct code for ExternalModuleImport with single quote 1`] = `"import externalAlias = require('externalModuleLib');"`; @@ -107,6 +139,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -133,6 +168,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -144,9 +182,41 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor } from 'defaultWithNamedMultilineImport';" `; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 1`] = `"import { spec1, spec2 as alias2 } from \\"namedLib\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 1`] = `"import { spec1, spec2 as alias2 } from 'namedLib';"`; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 2`] = ` +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 2`] = `"import { spec1, spec2 as alias2 } from \\"namedLib\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 3`] = ` +"import { + spec1, + spec2 as alias2, +} from \\"namedLib\\";" +`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 4`] = ` +"import { + spec1, + spec10, + spec11, + spec12, + spec13, + spec14, + spec15, + spec16, + spec17, + spec18, + spec2, + spec3, + spec4, + spec5, + spec6, + spec7, + spec8, + spec9, +} from 'multiLineNamedLib';" +`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 5`] = ` "import { spec1, spec10, @@ -155,6 +225,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -166,13 +239,99 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor } from \\"multiLineNamedLib\\";" `; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 3`] = `"import { } from \\"emptyImport\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 6`] = ` +"import { + spec1, + spec10, + spec11, + spec12, + spec13, + spec14, + spec15, + spec16, + spec17, + spec18, + spec2, + spec3, + spec4, + spec5, + spec6, + spec7, + spec8, + spec9, +} from \\"multiLineNamedLib\\";" +`; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 4`] = `"import Default from \\"defaultImport\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 7`] = `"import { } from 'emptyImport';"`; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 5`] = `"import Default, { spec1, spec2 as alias2 } from \\"defaultWithNamedImport\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 8`] = `"import { } from \\"emptyImport\\";"`; -exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 6`] = ` +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 9`] = `"import { } from \\"emptyImport\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 10`] = `"import Default from 'defaultImport';"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 11`] = `"import Default from \\"defaultImport\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 12`] = `"import Default from \\"defaultImport\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 13`] = `"import Default, { spec1, spec2 as alias2 } from 'defaultWithNamedImport';"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 14`] = `"import Default, { spec1, spec2 as alias2 } from \\"defaultWithNamedImport\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 15`] = ` +"import Default, { + spec1, + spec2 as alias2, +} from \\"defaultWithNamedImport\\";" +`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 16`] = ` +"import Default, { + spec1, + spec10, + spec11, + spec12, + spec13, + spec14, + spec15, + spec16, + spec17, + spec18, + spec2, + spec3, + spec4, + spec5, + spec6, + spec7, + spec8, + spec9, +} from 'defaultWithNamedMultilineImport';" +`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 17`] = ` +"import Default, { + spec1, + spec10, + spec11, + spec12, + spec13, + spec14, + spec15, + spec16, + spec17, + spec18, + spec2, + spec3, + spec4, + spec5, + spec6, + spec7, + spec8, + spec9, +} from \\"defaultWithNamedMultilineImport\\";" +`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamedImport with double quote 18`] = ` "import Default, { spec1, spec10, @@ -181,6 +340,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -203,6 +365,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -229,6 +394,9 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor spec13, spec14, spec15, + spec16, + spec17, + spec18, spec2, spec3, spec4, @@ -242,7 +410,11 @@ exports[`TypescriptCodeGenerator should generate the correct code for NamedImpor exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport 1`] = `"import * as namespaceAlias from 'namespaceLib';"`; -exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport with double quote 1`] = `"import * as namespaceAlias from \\"namespaceLib\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport with double quote 1`] = `"import * as namespaceAlias from 'namespaceLib';"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport with double quote 2`] = `"import * as namespaceAlias from \\"namespaceLib\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport with double quote 3`] = `"import * as namespaceAlias from \\"namespaceLib\\";"`; exports[`TypescriptCodeGenerator should generate the correct code for NamespaceImport with single quote 1`] = `"import * as namespaceAlias from 'namespaceLib';"`; @@ -315,7 +487,11 @@ exports[`TypescriptCodeGenerator should generate the correct code for SetterDecl exports[`TypescriptCodeGenerator should generate the correct code for StringImport 1`] = `"import 'stringLib';"`; -exports[`TypescriptCodeGenerator should generate the correct code for StringImport with double quote 1`] = `"import \\"stringLib\\";"`; +exports[`TypescriptCodeGenerator should generate the correct code for StringImport with double quote 1`] = `"import 'stringLib';"`; + +exports[`TypescriptCodeGenerator should generate the correct code for StringImport with double quote 2`] = `"import \\"stringLib\\";"`; + +exports[`TypescriptCodeGenerator should generate the correct code for StringImport with double quote 3`] = `"import \\"stringLib\\";"`; exports[`TypescriptCodeGenerator should generate the correct code for StringImport with single quote 1`] = `"import 'stringLib';"`; diff --git a/test/testUtilities.ts b/test/testUtilities.ts index fd2a6f7..aa2f8a3 100644 --- a/test/testUtilities.ts +++ b/test/testUtilities.ts @@ -1,4 +1,5 @@ import * as Path from 'path'; + import { toPosix } from '../src/utilities/PathHelpers'; export function getWorkspaceFile(pathFromWorkspace: string): string { diff --git a/tslint.json b/tslint.json index 15aade3..bd3724a 100644 --- a/tslint.json +++ b/tslint.json @@ -1,53 +1,14 @@ { "extends": [ - "tslint-config-airbnb" + "@smartive/tslint-config" ], "rules": { - "max-line-length": [ - true, - 125 - ], - "member-ordering": [ - true, - { - "order": [ - "public-static-field", - "protected-static-field", - "private-static-field", - "public-instance-field", - "protected-instance-field", - "private-instance-field", - "constructor", - "public-static-method", - "protected-static-method", - "private-static-method", - "public-instance-method", - "protected-instance-method", - "private-instance-method" - ] - } - ], - "no-boolean-literal-compare": false, - "strict-boolean-expressions": false, "ter-indent": [ true, 4, { "SwitchCase": 1 } - ], - "typedef": [ - true, - "call-signature", - "parameter", - "property-declaration", - "member-variable-declaration" - ], - "variable-name": [ - true, - "check-format", - "allow-leading-underscore", - "allow-pascal-case" ] } }