From 21ed6bbb636a7d04d74ba62ace6248acab6d2c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20R=C3=A4ntil=C3=A4?= Date: Sun, 5 Feb 2023 09:51:26 +0100 Subject: [PATCH] feat(typescript): Added namespace support Introduces --ts-namespaces option fix #10 --- README.md | 51 ++++++++++++++++++++++++- lib/batch-convert.spec.ts | 14 +++---- lib/bin/typeconv.ts | 78 +++++++++++++++++++++++++++++++++++++-- package.json | 2 +- yarn.lock | 12 +++++- 5 files changed, 144 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8355121..76dca5c 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,48 @@ Usage: typeconv [options] file ... --(no-)ts-disable-lint-header Output comments for disabling linting (default: true) --(no-)ts-descriptive-header Output the header comment (default: true) --(no-)ts-use-unknown Use 'unknown' type instead of 'any' (default: true) + --ts-non-exported Strategy for non-exported types (default: include-if-referenced) + + Values: + fail Fail conversion + ignore Don't include non-exported types, + even if referenced + include Include non-exported types + inline Don't include non-exported types, + inline them if necessary. + Will fail on cyclic types + include-if-referenced Include non-exported types only if they + are referenced from exported types + + --ts-namespaces Namespace strategy. (default: ignore) + + Values: + ignore Ignore namespaces entirely (default). + - When converting from TypeScript, types in namespaces + aren't exported. + - When converting to TypeScript, no attempt to + reconstruct namespaces is performed. + hoist When converting from TypeScript, hoist types inside + namespaces to top-level, so that the types are + included, but without their namespace. + This can cause conflicts, in which case deeper + declarations will be dropped in favor of more top- + level declarations. + In case of same-level (namespace depth) declarations + with the same name, only one will be exported in a + non-deterministic manner. + dot When converting from TypeScript, join the namespaces + and the exported type with a dot (.). + When converting to TypeScript, try to reconstruct + namespaces by splitting the name on dot (.). + underscore When converting from TypeScript, join the namespaces + and the exported type with an underscore (_). + When converting to TypeScript, try to reconstruct + namespaces by splitting the name on underscore (_). + reconstruct-all When converting to TypeScript, try to reconstruct + namespaces by splitting the name on both dot and + underscore. + GraphQL --gql-unsupported Method to use for unsupported types @@ -222,10 +264,17 @@ Usage: typeconv [options] file ... --st-ref-method SureType reference export method (default: provided) Values: - no-refs Don't ref anything, inline all types. + no-refs Don't ref anything, inline all types provided Reference types that are explicitly exported ref-all Ref all provided types and those with names + --st-missing-ref What to do when detecting an unresolvable reference (default: warn) + + Values: + ignore Ignore; skip type or cast to any + warn Same as 'ignore', but warn + error Fail conversion + --(no-)st-inline-types Inline pretty typescript types aside validator code (default: true) --(no-)st-export-type Export the deduced types (or the pretty types, depending on --st-inline-types) diff --git a/lib/batch-convert.spec.ts b/lib/batch-convert.spec.ts index b0b8624..ed38621 100644 --- a/lib/batch-convert.spec.ts +++ b/lib/batch-convert.spec.ts @@ -38,8 +38,8 @@ describe( "batch-convert", ( ) => ); expect( log.mock.calls.length ).toBe( 2 ); - const types1 = JSON.parse( log.mock.calls[ 0 ] ); - const types2 = JSON.parse( log.mock.calls[ 1 ] ); + const types1 = JSON.parse( log.mock.calls[ 0 ] as any ); + const types2 = JSON.parse( log.mock.calls[ 1 ] as any ); expect( types1 ).toMatchSnapshot( ); expect( types2 ).toMatchSnapshot( ); @@ -62,8 +62,8 @@ describe( "batch-convert", ( ) => ); expect( log.mock.calls.length ).toBe( 2 ); - const types1 = JSON.parse( log.mock.calls[ 0 ] ); - const types2 = JSON.parse( log.mock.calls[ 1 ] ); + const types1 = JSON.parse( log.mock.calls[ 0 ] as any ); + const types2 = JSON.parse( log.mock.calls[ 1 ] as any ); expect( types1 ).toMatchSnapshot( ); expect( types2 ).toMatchSnapshot( ); @@ -74,7 +74,7 @@ describe( "batch-convert", ( ) => .map( ( line, i ) => i === 0 // Absolute local files system, must be aligned with CI/CD - ? path.basename( line[ 0 ] ) + ? path.basename( line[ 0 ] as any ) : line ) ).toMatchSnapshot( ); @@ -94,8 +94,8 @@ describe( "batch-convert", ( ) => ); expect( log.mock.calls.length ).toBe( 2 ); - const types1 = JSON.parse( log.mock.calls[ 0 ] ); - const types2 = JSON.parse( log.mock.calls[ 1 ] ); + const types1 = JSON.parse( log.mock.calls[ 0 ] as any ); + const types2 = JSON.parse( log.mock.calls[ 1 ] as any ); expect( types1 ).toMatchSnapshot( ); expect( types2 ).toMatchSnapshot( ); diff --git a/lib/bin/typeconv.ts b/lib/bin/typeconv.ts index cfae804..08496ae 100644 --- a/lib/bin/typeconv.ts +++ b/lib/bin/typeconv.ts @@ -3,9 +3,9 @@ import chalk from "chalk" import { oppa } from "oppa" import { stripAnnotations } from "core-types" -import { JsonSchemaToSuretypeOptions } from 'core-types-suretype' -import { FromTsOptions } from "core-types-ts" -import { ExportRefMethod } from "suretype" +import type { JsonSchemaToSuretypeOptions } from "core-types-suretype" +import type { FromTsOptions, ToTsOptions } from "core-types-ts" +import type { ExportRefMethod } from "suretype" import { makeConverter } from "../converter.js" import { batchConvertGlob } from "../batch-convert.js" @@ -192,6 +192,52 @@ const oppaInstance = ] }, ], } ) + .add( { + name: "ts-namespaces", + type: "string", + argumentName: "method", + description: [ + "Namespace strategy." + ], + default: "ignore", + values: [ + { "ignore": [ + "Ignore namespaces entirely (default).", + "- When converting from TypeScript, types in namespaces", + "aren't exported.", + "- When converting to TypeScript, no attempt to", + "reconstruct namespaces is performed.", + ] }, + { "hoist": [ + "When converting from TypeScript, hoist types inside", + "namespaces to top-level, so that the types are", + "included, but without their namespace.", + "This can cause conflicts, in which case deeper", + "declarations will be dropped in favor of more top-", + "level declarations.", + "In case of same-level (namespace depth) declarations", + "with the same name, only one will be exported in a", + "non-deterministic manner.", + ] }, + { "dot": [ + "When converting from TypeScript, join the namespaces", + "and the exported type with a dot (.).", + "When converting to TypeScript, try to reconstruct", + "namespaces by splitting the name on dot (.).", + ] }, + { "underscore": [ + "When converting from TypeScript, join the namespaces", + "and the exported type with an underscore (_).", + "When converting to TypeScript, try to reconstruct", + "namespaces by splitting the name on underscore (_).", + ] }, + { "reconstruct-all": [ + "When converting to TypeScript, try to reconstruct", + "namespaces by splitting the name on both dot and", + "underscore.", + ] }, + ], + } ) .group( { name: "GraphQL", @@ -354,6 +400,7 @@ const { "ts-descriptive-header": tsDescriptiveHeader, "ts-use-unknown": tsUseUnknown, "ts-non-exported": tsNonExported, + "ts-namespaces": tsNamespaces, // JSON Schema @@ -407,6 +454,29 @@ if ( !ensureType< FromTsOptions[ 'nonExported' ] >( ) ) throw new Error( ); + +if ( !ensureType< + 'ignore' | 'hoist' | 'dot' | 'underscore' | 'reconstruct-all' + >( + tsNamespaces, + 'ts-namespaces', + [ 'ignore', 'hoist', 'dot', 'underscore', 'reconstruct-all' ], + printHelp +) ) + throw new Error( ); + +const toTsNamespaces: ToTsOptions[ 'namespaces' ] = + tsNamespaces === 'reconstruct-all' ? 'all' : + tsNamespaces === 'dot' ? 'dot' : + tsNamespaces === 'underscore' ? 'underscore' : + 'ignore'; + +const fromTsNamespaces: FromTsOptions[ 'namespaces' ] = + tsNamespaces === 'hoist' ? 'hoist' : + tsNamespaces === 'dot' ? 'join-dot' : + tsNamespaces === 'underscore' ? 'join-underscore' : + 'ignore'; + if ( !ensureType< ExportRefMethod | undefined >( stRefMethod, 'ref-method', @@ -428,6 +498,7 @@ const getReader = ( ): Reader => return fromType === 'ts' ? getTypeScriptReader( { nonExported: tsNonExported, + namespaces: fromTsNamespaces, } ) : fromType === 'jsc' ? getJsonSchemaReader( ) @@ -454,6 +525,7 @@ const getWriter = ( ): Writer => declaration: tsDeclaration, noDisableLintHeader: tsDisableLintHeader, noDescriptiveHeader: tsDescriptiveHeader, + namespaces: toTsNamespaces, useUnknown: tsUseUnknown, } ) : toType === 'jsc' diff --git a/package.json b/package.json index 7e7b4fa..e562e24 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "core-types-graphql": "^2.0.0", "core-types-json-schema": "^2.0.0", "core-types-suretype": "^3.1.0", - "core-types-ts": "^3.2.0", + "core-types-ts": "^3.3.0", "globby": "^13.1.3", "js-yaml": "^4.1.0", "oppa": "^0.4.0", diff --git a/yarn.lock b/yarn.lock index 21c9fab..6e57b8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3033,6 +3033,16 @@ __metadata: languageName: node linkType: hard +"core-types-ts@npm:^3.3.0": + version: 3.3.0 + resolution: "core-types-ts@npm:3.3.0" + dependencies: + core-types: ^2.0.1 + typescript: ^4.9.5 + checksum: f16a340f65e0ea6d4aa8241508de064fb3cc3f87f4ed5ae80a14ab34170a8bd0eae92ea537c4fb702e392df88c13ff6a8858823c3a3707007960c564223007b5 + languageName: node + linkType: hard + "core-types@npm:^2.0.1": version: 2.0.1 resolution: "core-types@npm:2.0.1" @@ -6303,7 +6313,7 @@ __metadata: core-types-graphql: ^2.0.0 core-types-json-schema: ^2.0.0 core-types-suretype: ^3.1.0 - core-types-ts: ^3.2.0 + core-types-ts: ^3.3.0 cz-conventional-changelog: ^3.3.0 execa: ^6.1.0 globby: ^13.1.3