From 39b06bae361217c1fa28a9ba29ffe02174501986 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 30 Sep 2021 14:31:04 +0200 Subject: [PATCH 1/2] chore(rosetta): change prettier line width to 120 Prettier is adding a lot of line breaks that are cluttering up the code. Set the line width hint to 120, just for the Rosetta subproject. --- .prettierrc.yaml | 3 + packages/jsii-rosetta/bin/jsii-rosetta.ts | 72 +-- packages/jsii-rosetta/lib/commands/convert.ts | 20 +- packages/jsii-rosetta/lib/commands/extract.ts | 49 +-- .../lib/commands/extract_worker.ts | 13 +- packages/jsii-rosetta/lib/commands/read.ts | 16 +- .../lib/commands/transliterate.ts | 25 +- packages/jsii-rosetta/lib/fixtures.ts | 48 +- packages/jsii-rosetta/lib/jsii/assemblies.ts | 51 +-- packages/jsii-rosetta/lib/jsii/jsii-utils.ts | 10 +- packages/jsii-rosetta/lib/languages/csharp.ts | 382 ++++------------ .../jsii-rosetta/lib/languages/default.ts | 231 ++-------- packages/jsii-rosetta/lib/languages/java.ts | 413 ++++-------------- packages/jsii-rosetta/lib/languages/python.ts | 369 ++++------------ .../jsii-rosetta/lib/languages/visualize.ts | 186 ++------ packages/jsii-rosetta/lib/logging.ts | 4 +- .../lib/markdown/extract-snippets.ts | 4 +- .../lib/markdown/javadoc-renderer.ts | 20 +- .../lib/markdown/markdown-renderer.ts | 11 +- .../jsii-rosetta/lib/markdown/markdown.ts | 11 +- .../lib/markdown/replace-code-renderer.ts | 3 +- .../markdown/replace-typescript-transform.ts | 16 +- .../lib/markdown/xml-comment-renderer.ts | 8 +- packages/jsii-rosetta/lib/o-tree.ts | 16 +- packages/jsii-rosetta/lib/renderer.ts | 211 ++------- packages/jsii-rosetta/lib/rosetta.ts | 45 +- packages/jsii-rosetta/lib/snippet.ts | 22 +- packages/jsii-rosetta/lib/tablets/tablets.ts | 34 +- packages/jsii-rosetta/lib/translate.ts | 78 +--- .../jsii-rosetta/lib/typescript/ast-utils.ts | 107 +---- .../jsii-rosetta/lib/typescript/imports.ts | 37 +- .../lib/typescript/ts-compiler.ts | 48 +- packages/jsii-rosetta/lib/typescript/types.ts | 24 +- packages/jsii-rosetta/lib/util.ts | 18 +- .../test/commands/transliterate.test.ts | 52 +-- packages/jsii-rosetta/test/fixtures.test.ts | 3 +- .../jsii-rosetta/test/jsii/assemblies.test.ts | 43 +- .../jsii-rosetta/test/jsii/astutils.test.ts | 12 +- .../test/markdown/roundtrip.test.ts | 4 +- packages/jsii-rosetta/test/otree.test.ts | 26 +- packages/jsii-rosetta/test/rosetta.test.ts | 38 +- packages/jsii-rosetta/test/translate.test.ts | 3 +- .../jsii-rosetta/test/translations.test.ts | 9 +- packages/jsii-rosetta/test/util.test.ts | 25 +- 44 files changed, 606 insertions(+), 2214 deletions(-) diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 9a8fa96093..bc70ddc9ae 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -14,3 +14,6 @@ overrides: singleQuote: true quoteProps: as-needed trailingComma: all + - files: "packages/jsii-rosetta/**" + options: + printWidth: 120 \ No newline at end of file diff --git a/packages/jsii-rosetta/bin/jsii-rosetta.ts b/packages/jsii-rosetta/bin/jsii-rosetta.ts index 5f7c50e208..bae4341617 100644 --- a/packages/jsii-rosetta/bin/jsii-rosetta.ts +++ b/packages/jsii-rosetta/bin/jsii-rosetta.ts @@ -4,11 +4,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import * as yargs from 'yargs'; -import { - TranslateResult, - DEFAULT_TABLET_NAME, - translateTypeScript, -} from '../lib'; +import { TranslateResult, DEFAULT_TABLET_NAME, translateTypeScript } from '../lib'; import { translateMarkdown } from '../lib/commands/convert'; import { extractSnippets } from '../lib/commands/extract'; import { readTablet } from '../lib/commands/read'; @@ -44,10 +40,7 @@ function main() { description: 'Translate snippets to Python', }), wrapHandler(async (args) => { - const result = translateTypeScript( - await makeFileSource(args.FILE ?? '-', 'stdin.ts'), - makeVisitor(args), - ); + const result = translateTypeScript(await makeFileSource(args.FILE ?? '-', 'stdin.ts'), makeVisitor(args)); renderResult(result); }), ) @@ -66,10 +59,7 @@ function main() { description: 'Translate snippets to Python', }), wrapHandler(async (args) => { - const result = translateMarkdown( - await makeFileSource(args.FILE ?? '-', 'stdin.md'), - makeVisitor(args), - ); + const result = translateMarkdown(await makeFileSource(args.FILE ?? '-', 'stdin.md'), makeVisitor(args)); renderResult(result); }), ) @@ -115,8 +105,7 @@ function main() { }) .option('validate-assemblies', { type: 'boolean', - describe: - 'Whether to validate loaded assemblies or not (this can be slow)', + describe: 'Whether to validate loaded assemblies or not (this can be slow)', default: false, }) .option('strict', { @@ -138,9 +127,7 @@ function main() { // chdir, since underneath the in-memory layer we're using a regular TS // compilerhost. Have to make all file references absolute before we chdir // though. - const absAssemblies = ( - args.ASSEMBLY.length > 0 ? args.ASSEMBLY : ['.'] - ).map((x) => path.resolve(x)); + const absAssemblies = (args.ASSEMBLY.length > 0 ? args.ASSEMBLY : ['.']).map((x) => path.resolve(x)); const absOutput = path.resolve(args.output); if (args.directory) { process.chdir(args.directory); @@ -156,16 +143,10 @@ function main() { printDiagnostics(result.diagnostics, process.stderr); if (result.diagnostics.length > 0) { - logging.warn( - `${result.diagnostics.length} diagnostics encountered in ${result.tablet.count} snippets`, - ); + logging.warn(`${result.diagnostics.length} diagnostics encountered in ${result.tablet.count} snippets`); } - if ( - result.diagnostics.some((diag) => - isErrorDiagnostic(diag, { onlyStrict: !args.fail }), - ) - ) { + if (result.diagnostics.some((diag) => isErrorDiagnostic(diag, { onlyStrict: !args.fail }))) { process.exitCode = 1; } }), @@ -199,8 +180,7 @@ function main() { .options('loose', { alias: 'l', conflicts: 'strict', - describe: - 'Ignore missing fixtures and literate markdown files instead of failing', + describe: 'Ignore missing fixtures and literate markdown files instead of failing', type: 'boolean', }) .option('tablet', { @@ -210,20 +190,16 @@ function main() { 'Language tablet containing pre-translated code examples to use (these are generated by the `extract` command)', }), wrapHandler((args) => { - const assemblies = ( - args.ASSEMBLY.length > 0 ? args.ASSEMBLY : ['.'] - ).map((dir) => path.resolve(process.cwd(), dir)); + const assemblies = (args.ASSEMBLY.length > 0 ? args.ASSEMBLY : ['.']).map((dir) => + path.resolve(process.cwd(), dir), + ); const languages = args.language.length > 0 ? args.language.map((lang) => { - const target = Object.entries(TargetLanguage).find( - ([k]) => k === lang, - )?.[1]; + const target = Object.entries(TargetLanguage).find(([k]) => k === lang)?.[1]; if (target == null) { throw new Error( - `Unknown target language: ${lang}. Expected one of ${Object.keys( - TargetLanguage, - ).join(', ')}`, + `Unknown target language: ${lang}. Expected one of ${Object.keys(TargetLanguage).join(', ')}`, ); } return target; @@ -282,8 +258,7 @@ function main() { // Nothing to do - it's already configured, so we assert idempotent success! return Promise.resolve(); } - const md = (packageJson.jsii.metadata = - packageJson.jsii.metadata ?? {}); + const md = (packageJson.jsii.metadata = packageJson.jsii.metadata ?? {}); const mdJsii = (md.jsii = md.jsii ?? {}); const mdRosetta = (mdJsii.rosetta = mdJsii.rosetta ?? {}); mdRosetta.strict = true; @@ -306,9 +281,7 @@ function main() { /** * Wrap a command's handler with standard pre- and post-work */ -function wrapHandler( - handler: (x: A) => Promise, -) { +function wrapHandler(handler: (x: A) => Promise) { return (argv: A) => { logging.configure({ level: argv.verbose !== undefined ? argv.verbose : 0 }); handler(argv).catch((e) => { @@ -325,10 +298,7 @@ function makeVisitor(args: { python?: boolean }) { return new VisualizeAstVisitor(); } -async function makeFileSource( - fileName: string, - stdinName: string, -): Promise { +async function makeFileSource(fileName: string, stdinName: string): Promise { if (fileName === '-') { return { contents: await readStdin(), @@ -355,9 +325,7 @@ async function readStdin(): Promise { }); process.stdin.on('error', reject); - process.stdin.on('end', () => - resolve(Buffer.concat(parts).toString('utf-8')), - ); + process.stdin.on('end', () => resolve(Buffer.concat(parts).toString('utf-8'))); }); } @@ -367,11 +335,7 @@ function renderResult(result: TranslateResult) { if (result.diagnostics.length > 0) { printDiagnostics(result.diagnostics, process.stderr); - if ( - result.diagnostics.some((diag) => - isErrorDiagnostic(diag, { onlyStrict: false }), - ) - ) { + if (result.diagnostics.some((diag) => isErrorDiagnostic(diag, { onlyStrict: false }))) { process.exit(1); } } diff --git a/packages/jsii-rosetta/lib/commands/convert.ts b/packages/jsii-rosetta/lib/commands/convert.ts index 6bcdae41e3..ff720711ed 100644 --- a/packages/jsii-rosetta/lib/commands/convert.ts +++ b/packages/jsii-rosetta/lib/commands/convert.ts @@ -27,19 +27,13 @@ export function translateMarkdown( const translatedMarkdown = transformMarkdown( markdown.contents, new MarkdownRenderer(), - new ReplaceTypeScriptTransform( - markdown.fileName, - opts.strict ?? false, - (tsSnippet) => { - const translated = translator - .translatorFor(tsSnippet) - .renderUsing(visitor); - return { - language: opts.languageIdentifier ?? '', - source: translated, - }; - }, - ), + new ReplaceTypeScriptTransform(markdown.fileName, opts.strict ?? false, (tsSnippet) => { + const translated = translator.translatorFor(tsSnippet).renderUsing(visitor); + return { + language: opts.languageIdentifier ?? '', + source: translated, + }; + }), ); return { diff --git a/packages/jsii-rosetta/lib/commands/extract.ts b/packages/jsii-rosetta/lib/commands/extract.ts index 08e7a7a4da..fb8f4c42dd 100644 --- a/packages/jsii-rosetta/lib/commands/extract.ts +++ b/packages/jsii-rosetta/lib/commands/extract.ts @@ -34,10 +34,7 @@ export async function extractSnippets( const only = options.only ?? []; logging.info(`Loading ${assemblyLocations.length} assemblies`); - const assemblies = await loadAssemblies( - assemblyLocations, - options.validateAssemblies, - ); + const assemblies = await loadAssemblies(assemblyLocations, options.validateAssemblies); let snippets = allTypeScriptSnippets(assemblies, loose); if (only.length > 0) { @@ -49,10 +46,7 @@ export async function extractSnippets( logging.info('Translating'); const startTime = Date.now(); - const result = await translateAll( - snippets, - options.includeCompilerDiagnostics, - ); + const result = await translateAll(snippets, options.includeCompilerDiagnostics); for (const snippet of result.translatedSnippets) { tablet.addSnippet(snippet); @@ -60,9 +54,7 @@ export async function extractSnippets( const delta = (Date.now() - startTime) / 1000; logging.info( - `Converted ${tablet.count} snippets in ${delta} seconds (${( - delta / tablet.count - ).toPrecision(3)}s/snippet)`, + `Converted ${tablet.count} snippets in ${delta} seconds (${(delta / tablet.count).toPrecision(3)}s/snippet)`, ); logging.info(`Saving language tablet to ${options.outputFile}`); await tablet.save(options.outputFile); @@ -78,10 +70,7 @@ interface TranslateAllResult { /** * Only yield the snippets whose id exists in a whitelist */ -function* filterSnippets( - ts: IterableIterator, - includeIds: string[], -) { +function* filterSnippets(ts: IterableIterator, includeIds: string[]) { for (const t of ts) { if (includeIds.includes(snippetKey(t))) { yield t; @@ -101,18 +90,12 @@ async function translateAll( try { const worker = await import('worker_threads'); - return await workerBasedTranslateAll( - worker, - snippets, - includeCompilerDiagnostics, - ); + return await workerBasedTranslateAll(worker, snippets, includeCompilerDiagnostics); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') { throw e; } - logging.warn( - 'Worker threads not available (use NodeJS >= 10.5 and --experimental-worker). Working sequentially.', - ); + logging.warn('Worker threads not available (use NodeJS >= 10.5 and --experimental-worker). Working sequentially.'); return singleThreadedTranslateAll(snippets, includeCompilerDiagnostics); } @@ -168,21 +151,15 @@ async function workerBasedTranslateAll( // Use about half the advertised cores because hyperthreading doesn't seem to help that // much (on my machine, using more than half the cores actually makes it slower). // Cap to a reasonable top-level limit to prevent thrash on machines with many, many cores. - const maxWorkers = parseInt( - process.env.JSII_ROSETTA_MAX_WORKER_COUNT ?? '16', - ); + const maxWorkers = parseInt(process.env.JSII_ROSETTA_MAX_WORKER_COUNT ?? '16'); const N = Math.min(maxWorkers, Math.max(1, Math.ceil(os.cpus().length / 2))); const snippetArr = Array.from(snippets); const groups = divideEvenly(N, snippetArr); - logging.info( - `Translating ${snippetArr.length} snippets using ${groups.length} workers`, - ); + logging.info(`Translating ${snippetArr.length} snippets using ${groups.length} workers`); // Run workers const responses = await Promise.all( - groups - .map((snippets) => ({ snippets, includeCompilerDiagnostics })) - .map(runWorker), + groups.map((snippets) => ({ snippets, includeCompilerDiagnostics })).map(runWorker), ); // Combine results @@ -198,9 +175,7 @@ async function workerBasedTranslateAll( // Hydrate TranslatedSnippets from data back to objects return { diagnostics: x.diagnostics, - translatedSnippets: x.translatedSnippetSchemas.map((s) => - TranslatedSnippet.fromSchema(s), - ), + translatedSnippets: x.translatedSnippetSchemas.map((s) => TranslatedSnippet.fromSchema(s)), }; /** @@ -213,9 +188,7 @@ async function workerBasedTranslateAll( const wrk = new worker.Worker(path.join(__dirname, 'extract_worker.js'), { resourceLimits: { // Note: V8 heap statistics are expressed in bytes, so we divide by 1MiB (1,048,576 bytes) - maxOldGenerationSizeMb: Math.ceil( - v8.getHeapStatistics().heap_size_limit / 1_048_576, - ), + maxOldGenerationSizeMb: Math.ceil(v8.getHeapStatistics().heap_size_limit / 1_048_576), }, workerData: request, }); diff --git a/packages/jsii-rosetta/lib/commands/extract_worker.ts b/packages/jsii-rosetta/lib/commands/extract_worker.ts index d351df9d31..22ec62f42c 100644 --- a/packages/jsii-rosetta/lib/commands/extract_worker.ts +++ b/packages/jsii-rosetta/lib/commands/extract_worker.ts @@ -20,16 +20,11 @@ export interface TranslateResponse { } function translateSnippet(request: TranslateRequest): TranslateResponse { - const result = singleThreadedTranslateAll( - request.snippets[Symbol.iterator](), - request.includeCompilerDiagnostics, - ); + const result = singleThreadedTranslateAll(request.snippets[Symbol.iterator](), request.includeCompilerDiagnostics); return { diagnostics: result.diagnostics, - translatedSnippetSchemas: result.translatedSnippets.map((s) => - s.toSchema(), - ), + translatedSnippetSchemas: result.translatedSnippets.map((s) => s.toSchema()), }; } @@ -38,9 +33,7 @@ if (worker.isMainThread) { // deal, but we want to be compatible with run modes where 'worker_threads' is not available // and by doing this people on platforms where 'worker_threads' is available don't accidentally // add a require(). - throw new Error( - 'This script should be run as a worker, not included directly.', - ); + throw new Error('This script should be run as a worker, not included directly.'); } const request = worker.workerData; diff --git a/packages/jsii-rosetta/lib/commands/read.ts b/packages/jsii-rosetta/lib/commands/read.ts index 8b5268b30f..a998f78ae6 100644 --- a/packages/jsii-rosetta/lib/commands/read.ts +++ b/packages/jsii-rosetta/lib/commands/read.ts @@ -1,15 +1,7 @@ import { TargetLanguage } from '../languages'; -import { - LanguageTablet, - TranslatedSnippet, - Translation, -} from '../tablets/tablets'; +import { LanguageTablet, TranslatedSnippet, Translation } from '../tablets/tablets'; -export async function readTablet( - tabletFile: string, - key?: string, - lang?: string, -) { +export async function readTablet(tabletFile: string, key?: string, lang?: string) { const tab = new LanguageTablet(); await tab.load(tabletFile); @@ -76,7 +68,5 @@ function center(str: string, n: number, fill: string) { const before = Math.floor((n - str.length) / 2); const after = Math.ceil((n - str.length) / 2); - return ( - fill.repeat(Math.max(before, 0)) + str + fill.repeat(Math.max(after, 0)) - ); + return fill.repeat(Math.max(before, 0)) + str + fill.repeat(Math.max(after, 0)); } diff --git a/packages/jsii-rosetta/lib/commands/transliterate.ts b/packages/jsii-rosetta/lib/commands/transliterate.ts index d53c69eb93..5b3cfbb013 100644 --- a/packages/jsii-rosetta/lib/commands/transliterate.ts +++ b/packages/jsii-rosetta/lib/commands/transliterate.ts @@ -83,25 +83,15 @@ export async function transliterateAssembly( transliterateType(type, rosetta, language, location, options.loose); } // eslint-disable-next-line no-await-in-loop - await writeJson( - resolve(location, `${SPEC_FILE_NAME}.${language}`), - result, - { spaces: 2 }, - ); + await writeJson(resolve(location, `${SPEC_FILE_NAME}.${language}`), result, { spaces: 2 }); const then = new Date().getTime(); - debug( - `Done transliterating ${result.name}@${ - result.version - } to ${language} after ${then - now} milliseconds`, - ); + debug(`Done transliterating ${result.name}@${result.version} to ${language} after ${then - now} milliseconds`); } } rosetta.printDiagnostics(process.stderr); if (rosetta.hasErrors && options.strict) { - throw new Error( - 'Strict mode is enabled and some examples failed compilation!', - ); + throw new Error('Strict mode is enabled and some examples failed compilation!'); } } @@ -204,12 +194,9 @@ function transliterateType( function transliterateDocs(docs: Docs | undefined) { if (docs?.example) { const snippet = fixturize( - typeScriptSnippetFromSource( - docs.example, - 'example', - true /* strict */, - { [SnippetParameters.$PROJECT_DIRECTORY]: workingDirectory }, - ), + typeScriptSnippetFromSource(docs.example, 'example', true /* strict */, { + [SnippetParameters.$PROJECT_DIRECTORY]: workingDirectory, + }), loose, ); const translation = rosetta.translateSnippet(snippet, language); diff --git a/packages/jsii-rosetta/lib/fixtures.ts b/packages/jsii-rosetta/lib/fixtures.ts index e87272c2ad..fbf7ef294a 100644 --- a/packages/jsii-rosetta/lib/fixtures.ts +++ b/packages/jsii-rosetta/lib/fixtures.ts @@ -1,21 +1,13 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import { - createSourceFile, - ScriptKind, - ScriptTarget, - SyntaxKind, -} from 'typescript'; +import { createSourceFile, ScriptKind, ScriptTarget, SyntaxKind } from 'typescript'; import { TypeScriptSnippet, SnippetParameters } from './snippet'; /** * Complete snippets with fixtures, if required */ -export function fixturize( - snippet: TypeScriptSnippet, - loose = false, -): TypeScriptSnippet { +export function fixturize(snippet: TypeScriptSnippet, loose = false): TypeScriptSnippet { let source = snippet.visibleSource; const parameters = snippet.parameters ?? {}; @@ -36,10 +28,7 @@ export function fixturize( throw ex; } } - parameters[SnippetParameters.$COMPILATION_DIRECTORY] = path.join( - directory, - path.dirname(literateSource), - ); + parameters[SnippetParameters.$COMPILATION_DIRECTORY] = path.join(directory, path.dirname(literateSource)); } else if (parameters[SnippetParameters.FIXTURE]) { // Explicitly requested fixture must exist, unless we are operating in loose mode source = loadAndSubFixture(directory, parameters.fixture, source, !loose); @@ -60,28 +49,16 @@ function loadLiterateSource(directory: string, literateFileName: string) { const exists = fs.existsSync(fullPath); if (!exists) { // This couldn't really happen in practice, but do the check anyway - throw new Error( - `Sample uses literate source ${literateFileName}, but not found: ${fullPath}`, - ); + throw new Error(`Sample uses literate source ${literateFileName}, but not found: ${fullPath}`); } return fs.readFileSync(fullPath, { encoding: 'utf-8' }); } -function loadAndSubFixture( - directory: string, - fixtureName: string, - source: string, - mustExist: boolean, -) { - const fixtureFileName = path.join( - directory, - `rosetta/${fixtureName}.ts-fixture`, - ); +function loadAndSubFixture(directory: string, fixtureName: string, source: string, mustExist: boolean) { + const fixtureFileName = path.join(directory, `rosetta/${fixtureName}.ts-fixture`); const exists = fs.existsSync(fixtureFileName); if (!exists && mustExist) { - throw new Error( - `Sample uses fixture ${fixtureName}, but not found: ${fixtureFileName}`, - ); + throw new Error(`Sample uses fixture ${fixtureName}, but not found: ${fixtureFileName}`); } if (!exists) { return source; @@ -139,20 +116,13 @@ function sidelineImports(source: string): { let imports = ''; let statements = ''; - const sourceFile = createSourceFile( - 'index.ts', - source, - ScriptTarget.Latest, - true, - ScriptKind.TS, - ); + const sourceFile = createSourceFile('index.ts', source, ScriptTarget.Latest, true, ScriptKind.TS); for (const statement of sourceFile.statements) { if ( statement.kind === SyntaxKind.ImportDeclaration || statement.kind === SyntaxKind.ImportEqualsDeclaration || (statement.kind === SyntaxKind.VariableStatement && - statement.getChildAt(0).getChildAt(0).kind === - SyntaxKind.DeclareKeyword) + statement.getChildAt(0).getChildAt(0).kind === SyntaxKind.DeclareKeyword) ) { imports += statement.getFullText(sourceFile); } else { diff --git a/packages/jsii-rosetta/lib/jsii/assemblies.ts b/packages/jsii-rosetta/lib/jsii/assemblies.ts index 0b87fea897..92e304b954 100644 --- a/packages/jsii-rosetta/lib/jsii/assemblies.ts +++ b/packages/jsii-rosetta/lib/jsii/assemblies.ts @@ -4,12 +4,7 @@ import * as path from 'path'; import { fixturize } from '../fixtures'; import { extractTypescriptSnippetsFromMarkdown } from '../markdown/extract-snippets'; -import { - TypeScriptSnippet, - typeScriptSnippetFromSource, - updateParameters, - SnippetParameters, -} from '../snippet'; +import { TypeScriptSnippet, typeScriptSnippetFromSource, updateParameters, SnippetParameters } from '../snippet'; import { enforcesStrictMode } from '../strict'; export interface LoadedAssembly { @@ -38,14 +33,9 @@ export async function loadAssemblies( } } -async function loadAssemblyFromFile( - filename: string, - validate: boolean, -): Promise { +async function loadAssemblyFromFile(filename: string, validate: boolean): Promise { const contents = await fs.readJSON(filename, { encoding: 'utf-8' }); - return validate - ? spec.validateAssembly(contents) - : (contents as spec.Assembly); + return validate ? spec.validateAssembly(contents) : (contents as spec.Assembly); } export type AssemblySnippetSource = @@ -55,9 +45,7 @@ export type AssemblySnippetSource = /** * Return all markdown and example snippets from the given assembly */ -export function allSnippetSources( - assembly: spec.Assembly, -): AssemblySnippetSource[] { +export function allSnippetSources(assembly: spec.Assembly): AssemblySnippetSource[] { const ret: AssemblySnippetSource[] = []; if (assembly.readme) { @@ -68,9 +56,7 @@ export function allSnippetSources( }); } - for (const [submoduleFqn, submodule] of Object.entries( - assembly.submodules ?? {}, - )) { + for (const [submoduleFqn, submodule] of Object.entries(assembly.submodules ?? {})) { if (submodule.readme) { ret.push({ type: 'markdown', @@ -85,17 +71,11 @@ export function allSnippetSources( emitDocs(type.docs, `${assembly.name}.${type.name}`); if (spec.isEnumType(type)) { - type.members.forEach((m) => - emitDocs(m.docs, `${assembly.name}.${type.name}.${m.name}`), - ); + type.members.forEach((m) => emitDocs(m.docs, `${assembly.name}.${type.name}.${m.name}`)); } if (spec.isClassOrInterfaceType(type)) { - (type.methods ?? []).forEach((m) => - emitDocs(m.docs, `${assembly.name}.${type.name}#${m.name}`), - ); - (type.properties ?? []).forEach((m) => - emitDocs(m.docs, `${assembly.name}.${type.name}#${m.name}`), - ); + (type.methods ?? []).forEach((m) => emitDocs(m.docs, `${assembly.name}.${type.name}#${m.name}`)); + (type.properties ?? []).forEach((m) => emitDocs(m.docs, `${assembly.name}.${type.name}#${m.name}`)); } }); } @@ -141,20 +121,13 @@ export function* allTypeScriptSnippets( for (const source of allSnippetSources(assembly)) { switch (source.type) { case 'literal': - const snippet = updateParameters( - typeScriptSnippetFromSource(source.source, source.where, strict), - { - [SnippetParameters.$PROJECT_DIRECTORY]: directory, - }, - ); + const snippet = updateParameters(typeScriptSnippetFromSource(source.source, source.where, strict), { + [SnippetParameters.$PROJECT_DIRECTORY]: directory, + }); yield fixturize(snippet, loose); break; case 'markdown': - for (const snippet of extractTypescriptSnippetsFromMarkdown( - source.markdown, - source.where, - strict, - )) { + for (const snippet of extractTypescriptSnippetsFromMarkdown(source.markdown, source.where, strict)) { const withDirectory = updateParameters(snippet, { [SnippetParameters.$PROJECT_DIRECTORY]: directory, }); diff --git a/packages/jsii-rosetta/lib/jsii/jsii-utils.ts b/packages/jsii-rosetta/lib/jsii/jsii-utils.ts index dd641fd305..40ad0aa827 100644 --- a/packages/jsii-rosetta/lib/jsii/jsii-utils.ts +++ b/packages/jsii-rosetta/lib/jsii/jsii-utils.ts @@ -26,10 +26,7 @@ export interface StructProperty { questionMark: boolean; } -export function propertiesOfStruct( - type: ts.Type, - context: AstRenderer, -): StructProperty[] { +export function propertiesOfStruct(type: ts.Type, context: AstRenderer): StructProperty[] { return type.isClassOrInterface() ? type.getProperties().map((s) => { let propType; @@ -37,10 +34,7 @@ export function propertiesOfStruct( const propSymbol = type.getProperty(s.name)!; const symbolDecl = propSymbol.valueDeclaration; - if ( - ts.isPropertyDeclaration(symbolDecl) || - ts.isPropertySignature(symbolDecl) - ) { + if (ts.isPropertyDeclaration(symbolDecl) || ts.isPropertySignature(symbolDecl)) { questionMark = symbolDecl.questionToken !== undefined; propType = symbolDecl.type && context.typeOfType(symbolDecl.type); } diff --git a/packages/jsii-rosetta/lib/languages/csharp.ts b/packages/jsii-rosetta/lib/languages/csharp.ts index 4b00a61ee2..27844823c3 100644 --- a/packages/jsii-rosetta/lib/languages/csharp.ts +++ b/packages/jsii-rosetta/lib/languages/csharp.ts @@ -102,10 +102,7 @@ export class CSharpVisitor extends DefaultVisitor { */ private readonly importedModuleSymbols = new Set(); - public mergeContext( - old: CSharpLanguageContext, - update: Partial, - ): CSharpLanguageContext { + public mergeContext(old: CSharpLanguageContext, update: Partial): CSharpLanguageContext { return Object.assign({}, old, update); } @@ -120,20 +117,14 @@ export class CSharpVisitor extends DefaultVisitor { } // Uppercase methods and properties, leave the rest as-is - if ( - renderer.currentContext.propertyOrMethod && - !renderer.currentContext.privatePropertyNames.includes(text) - ) { + if (renderer.currentContext.propertyOrMethod && !renderer.currentContext.privatePropertyNames.includes(text)) { text = ucFirst(text); } return new OTree([text]); } - public importStatement( - importStatement: ImportStatement, - context: CSharpRenderer, - ): OTree { + public importStatement(importStatement: ImportStatement, context: CSharpRenderer): OTree { const namespace = this.lookupModuleNamespace(importStatement.packageName); if (importStatement.imports.import === 'full') { this.importedModuleAliases.add(importStatement.imports.alias); @@ -141,10 +132,7 @@ export class CSharpVisitor extends DefaultVisitor { } if (importStatement.imports.import === 'selective') { const statements = []; - const [withoutAlias, withAlias] = partition( - importStatement.imports.elements, - (im) => im.alias === undefined, - ); + const [withoutAlias, withAlias] = partition(importStatement.imports.elements, (im) => im.alias === undefined); // If there's at least one import without an alias, emit a namespace import. if (withoutAlias) { @@ -157,11 +145,7 @@ export class CSharpVisitor extends DefaultVisitor { // For every aliased import, emit an aliasing 'using' statement for (const aliasedImport of withAlias) { - statements.push( - `using ${ucFirst(aliasedImport.alias!)} = ${namespace}.${ucFirst( - aliasedImport.sourceName, - )};`, - ); + statements.push(`using ${ucFirst(aliasedImport.alias!)} = ${namespace}.${ucFirst(aliasedImport.sourceName)};`); this.importedModuleSymbols.add(aliasedImport.alias!); } @@ -171,31 +155,19 @@ export class CSharpVisitor extends DefaultVisitor { return nimpl(importStatement.node, context); } - public functionDeclaration( - node: ts.FunctionDeclaration, - renderer: CSharpRenderer, - ): OTree { + public functionDeclaration(node: ts.FunctionDeclaration, renderer: CSharpRenderer): OTree { return this.functionLike(node, renderer); } - public constructorDeclaration( - node: ts.ConstructorDeclaration, - renderer: CSharpRenderer, - ): OTree { + public constructorDeclaration(node: ts.ConstructorDeclaration, renderer: CSharpRenderer): OTree { return this.functionLike(node, renderer, { isConstructor: true }); } - public methodDeclaration( - node: ts.MethodDeclaration, - renderer: CSharpRenderer, - ): OTree { + public methodDeclaration(node: ts.MethodDeclaration, renderer: CSharpRenderer): OTree { return this.functionLike(node, renderer); } - public methodSignature( - node: ts.MethodSignature, - renderer: CSharpRenderer, - ): OTree { + public methodSignature(node: ts.MethodSignature, renderer: CSharpRenderer): OTree { return new OTree( [ this.renderTypeNode(node.type, false, renderer), @@ -221,19 +193,13 @@ export class CSharpVisitor extends DefaultVisitor { const methodName = opts.isConstructor ? renderer.currentContext.currentClassName ?? 'MyClass' : renderer.updateContext({ propertyOrMethod: true }).convert(node.name); - const returnType = opts.isConstructor - ? '' - : this.renderTypeNode(node.type, false, renderer); + const returnType = opts.isConstructor ? '' : this.renderTypeNode(node.type, false, renderer); const baseConstructorCall = new Array(); if (opts.isConstructor) { const superCall = findSuperCall(node.body, renderer); if (superCall) { - baseConstructorCall.push( - ': base(', - this.argumentList(superCall.arguments, renderer), - ') ', - ); + baseConstructorCall.push(': base(', this.argumentList(superCall.arguments, renderer), ') '); } } @@ -260,10 +226,7 @@ export class CSharpVisitor extends DefaultVisitor { return ret; } - public propertyDeclaration( - node: ts.PropertyDeclaration, - renderer: CSharpRenderer, - ): OTree { + public propertyDeclaration(node: ts.PropertyDeclaration, renderer: CSharpRenderer): OTree { const vis = visibility(node); const propertyOrMethod = vis !== 'private'; // Capitalize non-private fields @@ -274,16 +237,10 @@ export class CSharpVisitor extends DefaultVisitor { vis, isReadOnly(node) ? ' readonly' : '', ' ', - this.renderTypeNode( - node.type, - node.questionToken !== undefined, - renderer, - ), + this.renderTypeNode(node.type, node.questionToken !== undefined, renderer), ' ', renderer.updateContext({ propertyOrMethod }).convert(node.name), - ...(node.initializer - ? [' = ', renderer.convert(node.initializer)] - : []), + ...(node.initializer ? [' = ', renderer.convert(node.initializer)] : []), ';', ], [], @@ -296,11 +253,7 @@ export class CSharpVisitor extends DefaultVisitor { [ vis, ' ', - this.renderTypeNode( - node.type, - node.questionToken !== undefined, - renderer, - ), + this.renderTypeNode(node.type, node.questionToken !== undefined, renderer), ' ', renderer.updateContext({ propertyOrMethod }).convert(node.name), ' ', @@ -311,10 +264,7 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public printStatement( - args: ts.NodeArray, - renderer: CSharpRenderer, - ) { + public printStatement(args: ts.NodeArray, renderer: CSharpRenderer) { const renderedArgs = args.length === 1 ? renderer.convertAll(args) @@ -331,18 +281,12 @@ export class CSharpVisitor extends DefaultVisitor { return new OTree(['Console.WriteLine(', ...renderedArgs, ')']); } - public superCallExpression( - _node: ts.CallExpression, - _renderer: CSharpRenderer, - ): OTree { + public superCallExpression(_node: ts.CallExpression, _renderer: CSharpRenderer): OTree { // super() call rendered as part of the constructor already return NO_SYNTAX; } - public stringLiteral( - node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, - renderer: CSharpRenderer, - ): OTree { + public stringLiteral(node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, renderer: CSharpRenderer): OTree { if (renderer.currentContext.stringAsIdentifier) { return this.identifier(node, renderer); } @@ -353,10 +297,7 @@ export class CSharpVisitor extends DefaultVisitor { return new OTree([JSON.stringify(node.text)]); } - public expressionStatement( - node: ts.ExpressionStatement, - renderer: CSharpRenderer, - ): OTree { + public expressionStatement(node: ts.ExpressionStatement, renderer: CSharpRenderer): OTree { const inner = renderer.convert(node.expression); if (inner.isEmpty) { return inner; @@ -364,10 +305,7 @@ export class CSharpVisitor extends DefaultVisitor { return new OTree([inner, ';'], [], { canBreakLine: true }); } - public propertyAccessExpression( - node: ts.PropertyAccessExpression, - renderer: CSharpRenderer, - ): OTree { + public propertyAccessExpression(node: ts.PropertyAccessExpression, renderer: CSharpRenderer): OTree { const lhs = renderer.textOf(node.expression); // Suppress the LHS of the dot operator if it's "this." (not necessary in C#) @@ -375,57 +313,29 @@ export class CSharpVisitor extends DefaultVisitor { const objectExpression = lhs === 'this' || this.importedModuleAliases.has(lhs) ? [] - : [ - renderer - .updateContext({ propertyOrMethod: false }) - .convert(node.expression), - '.', - ]; + : [renderer.updateContext({ propertyOrMethod: false }).convert(node.expression), '.']; - return new OTree([ - ...objectExpression, - renderer.updateContext({ propertyOrMethod: true }).convert(node.name), - ]); + return new OTree([...objectExpression, renderer.updateContext({ propertyOrMethod: true }).convert(node.name)]); } - public parameterDeclaration( - node: ts.ParameterDeclaration, - renderer: CSharpRenderer, - ): OTree { + public parameterDeclaration(node: ts.ParameterDeclaration, renderer: CSharpRenderer): OTree { return new OTree([ - this.renderTypeNode( - node.type, - node.questionToken !== undefined, - renderer, - ), + this.renderTypeNode(node.type, node.questionToken !== undefined, renderer), ' ', renderer.convert(node.name), - ...(parameterAcceptsUndefined( - node, - node.type && renderer.typeOfType(node.type), - ) + ...(parameterAcceptsUndefined(node, node.type && renderer.typeOfType(node.type)) ? ['=', node.initializer ? renderer.convert(node.initializer) : 'null'] : []), ]); } - public propertySignature( - node: ts.PropertySignature, - renderer: CSharpRenderer, - ): OTree { - const canSet = - renderer.currentContext.inStructInterface || !isReadOnly(node); + public propertySignature(node: ts.PropertySignature, renderer: CSharpRenderer): OTree { + const canSet = renderer.currentContext.inStructInterface || !isReadOnly(node); return new OTree( [ - !renderer.currentContext.inRegularInterface - ? `${visibility(node)} ` - : NO_SYNTAX, - this.renderTypeNode( - node.type, - node.questionToken !== undefined, - renderer, - ), + !renderer.currentContext.inRegularInterface ? `${visibility(node)} ` : NO_SYNTAX, + this.renderTypeNode(node.type, node.questionToken !== undefined, renderer), ' ', renderer.updateContext({ propertyOrMethod: true }).convert(node.name), ' ', @@ -439,31 +349,18 @@ export class CSharpVisitor extends DefaultVisitor { /** * Do some work on property accesses to translate common JavaScript-isms to language-specific idioms */ - public regularCallExpression( - node: ts.CallExpression, - renderer: CSharpRenderer, - ): OTree { + public regularCallExpression(node: ts.CallExpression, renderer: CSharpRenderer): OTree { return new OTree([ - renderer - .updateContext({ propertyOrMethod: true }) - .convert(node.expression), + renderer.updateContext({ propertyOrMethod: true }).convert(node.expression), '(', this.argumentList(node.arguments, renderer), ')', ]); } - public classDeclaration( - node: ts.ClassDeclaration, - renderer: CSharpRenderer, - ): OTree { + public classDeclaration(node: ts.ClassDeclaration, renderer: CSharpRenderer): OTree { return new OTree( - [ - 'class ', - renderer.convert(node.name), - ...this.classHeritage(node, renderer), - '\n{', - ], + ['class ', renderer.convert(node.name), ...this.classHeritage(node, renderer), '\n{'], renderer .updateContext({ privatePropertyNames: privatePropertyNames(node.members, renderer), @@ -477,20 +374,10 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public structInterfaceDeclaration( - node: ts.InterfaceDeclaration, - renderer: CSharpRenderer, - ): OTree { + public structInterfaceDeclaration(node: ts.InterfaceDeclaration, renderer: CSharpRenderer): OTree { return new OTree( - [ - 'class ', - renderer.convert(node.name), - ...this.classHeritage(node, renderer), - '\n{', - ], - renderer - .updateContext({ inStructInterface: true }) - .convertAll(node.members), + ['class ', renderer.convert(node.name), ...this.classHeritage(node, renderer), '\n{'], + renderer.updateContext({ inStructInterface: true }).convertAll(node.members), { indent: 4, canBreakLine: true, @@ -499,20 +386,10 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public regularInterfaceDeclaration( - node: ts.InterfaceDeclaration, - renderer: CSharpRenderer, - ): OTree { + public regularInterfaceDeclaration(node: ts.InterfaceDeclaration, renderer: CSharpRenderer): OTree { return new OTree( - [ - 'interface ', - renderer.convert(node.name), - ...this.classHeritage(node, renderer), - '\n{', - ], - renderer - .updateContext({ inRegularInterface: true }) - .convertAll(node.members), + ['interface ', renderer.convert(node.name), ...this.classHeritage(node, renderer), '\n{'], + renderer.updateContext({ inRegularInterface: true }).convertAll(node.members), { indent: 4, canBreakLine: true, @@ -528,21 +405,14 @@ export class CSharpVisitor extends DefaultVisitor { }); } - public unknownTypeObjectLiteralExpression( - node: ts.ObjectLiteralExpression, - renderer: CSharpRenderer, - ): OTree { + public unknownTypeObjectLiteralExpression(node: ts.ObjectLiteralExpression, renderer: CSharpRenderer): OTree { if (renderer.currentContext.preferObjectLiteralAsStruct) { // Type information missing and from context we prefer a struct - return new OTree( - ['new Struct { '], - renderer.convertAll(node.properties), - { - suffix: renderer.mirrorNewlineBefore(node.properties[0], '}', ' '), - separator: ', ', - indent: 4, - }, - ); + return new OTree(['new Struct { '], renderer.convertAll(node.properties), { + suffix: renderer.mirrorNewlineBefore(node.properties[0], '}', ' '), + separator: ', ', + indent: 4, + }); } // Type information missing and from context we prefer a map return this.keyValueObjectLiteralExpression(node, undefined, renderer); @@ -553,15 +423,11 @@ export class CSharpVisitor extends DefaultVisitor { structType: ts.Type, renderer: CSharpRenderer, ): OTree { - return new OTree( - ['new ', structType.symbol.name, ' { '], - renderer.convertAll(node.properties), - { - suffix: renderer.mirrorNewlineBefore(node.properties[0], '}', ' '), - separator: ', ', - indent: 4, - }, - ); + return new OTree(['new ', structType.symbol.name, ' { '], renderer.convertAll(node.properties), { + suffix: renderer.mirrorNewlineBefore(node.properties[0], '}', ' '), + separator: ', ', + indent: 4, + }); } public keyValueObjectLiteralExpression( @@ -575,14 +441,8 @@ export class CSharpVisitor extends DefaultVisitor { } return new OTree( - [ - 'new Dictionary { ', - ], - renderer - .updateContext({ inKeyValueList: true }) - .convertAll(node.properties), + ['new Dictionary { '], + renderer.updateContext({ inKeyValueList: true }).convertAll(node.properties), { suffix: renderer.mirrorNewlineBefore(node.properties[0], '}', ' '), separator: ', ', @@ -591,25 +451,15 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - renderer: CSharpRenderer, - ): OTree { + public shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, renderer: CSharpRenderer): OTree { return this.renderPropertyAssignment(node.name, node.name, renderer); } - public propertyAssignment( - node: ts.PropertyAssignment, - renderer: CSharpRenderer, - ): OTree { + public propertyAssignment(node: ts.PropertyAssignment, renderer: CSharpRenderer): OTree { return this.renderPropertyAssignment(node.name, node.initializer, renderer); } - public renderPropertyAssignment( - key: ts.Node, - value: ts.Node, - renderer: CSharpRenderer, - ): OTree { + public renderPropertyAssignment(key: ts.Node, value: ts.Node, renderer: CSharpRenderer): OTree { if (renderer.currentContext.inKeyValueList) { return new OTree( [ @@ -630,9 +480,7 @@ export class CSharpVisitor extends DefaultVisitor { } return new OTree( [ - renderer - .updateContext({ propertyOrMethod: true, stringAsIdentifier: true }) - .convert(key), + renderer.updateContext({ propertyOrMethod: true, stringAsIdentifier: true }).convert(key), ' = ', renderer.convert(value), ], @@ -641,10 +489,7 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - renderer: CSharpRenderer, - ): OTree { + public arrayLiteralExpression(node: ts.ArrayLiteralExpression, renderer: CSharpRenderer): OTree { return new OTree(['new [] { '], renderer.convertAll(node.elements), { separator: ', ', suffix: ' }', @@ -672,50 +517,30 @@ export class CSharpVisitor extends DefaultVisitor { : ifStmt; } - public forOfStatement( - node: ts.ForOfStatement, - renderer: CSharpRenderer, - ): OTree { + public forOfStatement(node: ts.ForOfStatement, renderer: CSharpRenderer): OTree { // This is what a "for (const x of ...)" looks like in the AST let variableName = '???'; matchAst( node.initializer, - nodeOfType( - ts.SyntaxKind.VariableDeclarationList, - nodeOfType('var', ts.SyntaxKind.VariableDeclaration), - ), + nodeOfType(ts.SyntaxKind.VariableDeclarationList, nodeOfType('var', ts.SyntaxKind.VariableDeclaration)), (bindings) => { variableName = renderer.textOf(bindings.var.name); }, ); return new OTree( - [ - 'for (var ', - variableName, - ' in ', - renderer.convert(node.expression), - ') ', - ], + ['for (var ', variableName, ' in ', renderer.convert(node.expression), ') '], [renderer.convert(node.statement)], { canBreakLine: true }, ); } public asExpression(node: ts.AsExpression, context: CSharpRenderer): OTree { - return new OTree([ - '(', - this.renderTypeNode(node.type, false, context), - ')', - context.convert(node.expression), - ]); + return new OTree(['(', this.renderTypeNode(node.type, false, context), ')', context.convert(node.expression)]); } - public variableDeclaration( - node: ts.VariableDeclaration, - renderer: CSharpRenderer, - ): OTree { + public variableDeclaration(node: ts.VariableDeclaration, renderer: CSharpRenderer): OTree { const type = (node.type && renderer.typeOfType(node.type)) || (node.initializer && renderer.typeOfExpression(node.initializer)); @@ -731,9 +556,7 @@ export class CSharpVisitor extends DefaultVisitor { ' ', renderer.convert(node.name), ' = ', - renderer - .updateContext({ preferObjectLiteralAsStruct: false }) - .convert(node.initializer), + renderer.updateContext({ preferObjectLiteralAsStruct: false }).convert(node.initializer), ';', ], [], @@ -741,51 +564,30 @@ export class CSharpVisitor extends DefaultVisitor { ); } - public templateExpression( - node: ts.TemplateExpression, - context: CSharpRenderer, - ): OTree { + public templateExpression(node: ts.TemplateExpression, context: CSharpRenderer): OTree { // If this is a multi-line string literal, we need not quote much, as @"string" literals in C# // do not perform any quoting. The literal quotes in the text however must be doubled. const isMultiLine = - !!node.head.rawText?.includes('\n') || - node.templateSpans.some((span) => span.literal.rawText?.includes('\n')); + !!node.head.rawText?.includes('\n') || node.templateSpans.some((span) => span.literal.rawText?.includes('\n')); const parts = new Array(); if (node.head.rawText) { - parts.push( - isMultiLine - ? node.head.rawText.replace(/"/g, '""') - : quoteStringLiteral(node.head.rawText), - ); + parts.push(isMultiLine ? node.head.rawText.replace(/"/g, '""') : quoteStringLiteral(node.head.rawText)); } for (const span of node.templateSpans) { parts.push(`{${context.textOf(span.expression)}}`); if (span.literal.rawText) { - parts.push( - isMultiLine - ? span.literal.rawText.replace(/"/g, '""') - : quoteStringLiteral(span.literal.rawText), - ); + parts.push(isMultiLine ? span.literal.rawText.replace(/"/g, '""') : quoteStringLiteral(span.literal.rawText)); } } return new OTree([isMultiLine ? '$@"' : '$"', ...parts, '"']); } - protected argumentList( - args: readonly ts.Node[] | undefined, - renderer: CSharpRenderer, - ): OTree { - return new OTree( - [], - args - ? renderer - .updateContext({ preferObjectLiteralAsStruct: true }) - .convertAll(args) - : [], - { separator: ', ' }, - ); + protected argumentList(args: readonly ts.Node[] | undefined, renderer: CSharpRenderer): OTree { + return new OTree([], args ? renderer.updateContext({ preferObjectLiteralAsStruct: true }).convertAll(args) : [], { + separator: ', ', + }); } protected lookupModuleNamespace(ref: string) { @@ -803,21 +605,11 @@ export class CSharpVisitor extends DefaultVisitor { ); } - private renderTypeNode( - typeNode: ts.TypeNode | undefined, - questionMark: boolean, - renderer: CSharpRenderer, - ): string { + private renderTypeNode(typeNode: ts.TypeNode | undefined, questionMark: boolean, renderer: CSharpRenderer): string { if (!typeNode) { return 'void'; } - return this.renderType( - typeNode, - renderer.typeOfType(typeNode), - questionMark, - renderer.textOf(typeNode), - renderer, - ); + return this.renderType(typeNode, renderer.typeOfType(typeNode), questionMark, renderer.textOf(typeNode), renderer); } private renderType( @@ -839,32 +631,18 @@ export class CSharpVisitor extends DefaultVisitor { const mappedTo = mapElementType(nonUnionType, renderer); if (mappedTo) { - return `IDictionary`; + return `IDictionary`; } - return ( - typeNameFromType(nonUnionType, fallback) + - (typeContainsUndefined(type) || questionMark ? '?' : '') - ); + return typeNameFromType(nonUnionType, fallback) + (typeContainsUndefined(type) || questionMark ? '?' : ''); } - private classHeritage( - node: ts.ClassDeclaration | ts.InterfaceDeclaration, - renderer: CSharpRenderer, - ) { - const heritage = flat( - Array.from(node.heritageClauses ?? []).map((h) => Array.from(h.types)), - ).map((t) => renderer.convert(t.expression)); + private classHeritage(node: ts.ClassDeclaration | ts.InterfaceDeclaration, renderer: CSharpRenderer) { + const heritage = flat(Array.from(node.heritageClauses ?? []).map((h) => Array.from(h.types))).map((t) => + renderer.convert(t.expression), + ); - return heritage.length > 0 - ? [' : ', new OTree([], heritage, { separator: ', ' })] - : []; + return heritage.length > 0 ? [' : ', new OTree([], heritage, { separator: ', ' })] : []; } } diff --git a/packages/jsii-rosetta/lib/languages/default.ts b/packages/jsii-rosetta/lib/languages/default.ts index 802aa63e8c..36c79ce586 100644 --- a/packages/jsii-rosetta/lib/languages/default.ts +++ b/packages/jsii-rosetta/lib/languages/default.ts @@ -31,24 +31,15 @@ export abstract class DefaultVisitor implements AstHandler { return new OTree([]); } - public importStatement( - node: ImportStatement, - context: AstRenderer, - ): OTree { + public importStatement(node: ImportStatement, context: AstRenderer): OTree { return this.notImplemented(node.node, context); } - public functionDeclaration( - node: ts.FunctionDeclaration, - children: AstRenderer, - ): OTree { + public functionDeclaration(node: ts.FunctionDeclaration, children: AstRenderer): OTree { return this.notImplemented(node, children); } - public stringLiteral( - node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, - _renderer: AstRenderer, - ): OTree { + public stringLiteral(node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, _renderer: AstRenderer): OTree { return new OTree([JSON.stringify(node.text)]); } @@ -63,24 +54,15 @@ export abstract class DefaultVisitor implements AstHandler { }); } - public parameterDeclaration( - node: ts.ParameterDeclaration, - children: AstRenderer, - ): OTree { + public parameterDeclaration(node: ts.ParameterDeclaration, children: AstRenderer): OTree { return this.notImplemented(node, children); } - public returnStatement( - node: ts.ReturnStatement, - children: AstRenderer, - ): OTree { + public returnStatement(node: ts.ReturnStatement, children: AstRenderer): OTree { return new OTree(['return ', children.convert(node.expression)]); } - public binaryExpression( - node: ts.BinaryExpression, - context: AstRenderer, - ): OTree { + public binaryExpression(node: ts.BinaryExpression, context: AstRenderer): OTree { return new OTree([ context.convert(node.left), ' ', @@ -90,10 +72,7 @@ export abstract class DefaultVisitor implements AstHandler { ]); } - public prefixUnaryExpression( - node: ts.PrefixUnaryExpression, - context: AstRenderer, - ): OTree { + public prefixUnaryExpression(node: ts.PrefixUnaryExpression, context: AstRenderer): OTree { return new OTree([UNARY_OPS[node.operator], context.convert(node.operand)]); } @@ -101,24 +80,14 @@ export abstract class DefaultVisitor implements AstHandler { return this.notImplemented(node, context); } - public propertyAccessExpression( - node: ts.PropertyAccessExpression, - context: AstRenderer, - ): OTree { - return new OTree([ - context.convert(node.expression), - '.', - context.convert(node.name), - ]); + public propertyAccessExpression(node: ts.PropertyAccessExpression, context: AstRenderer): OTree { + return new OTree([context.convert(node.expression), '.', context.convert(node.name)]); } /** * Do some work on property accesses to translate common JavaScript-isms to language-specific idioms */ - public callExpression( - node: ts.CallExpression, - context: AstRenderer, - ): OTree { + public callExpression(node: ts.CallExpression, context: AstRenderer): OTree { const functionText = context.textOf(node.expression); if (functionText === 'console.log' || functionText === 'console.error') { return this.printStatement(node.arguments, context); @@ -130,45 +99,25 @@ export abstract class DefaultVisitor implements AstHandler { return this.regularCallExpression(node, context); } - public regularCallExpression( - node: ts.CallExpression, - context: AstRenderer, - ): OTree { - return new OTree([ - context.convert(node.expression), - '(', - this.argumentList(node.arguments, context), - ')', - ]); + public regularCallExpression(node: ts.CallExpression, context: AstRenderer): OTree { + return new OTree([context.convert(node.expression), '(', this.argumentList(node.arguments, context), ')']); } - public superCallExpression( - node: ts.CallExpression, - context: AstRenderer, - ): OTree { + public superCallExpression(node: ts.CallExpression, context: AstRenderer): OTree { return this.regularCallExpression(node, context); } - public printStatement( - args: ts.NodeArray, - context: AstRenderer, - ) { + public printStatement(args: ts.NodeArray, context: AstRenderer) { return new OTree(['', '(', this.argumentList(args, context), ')']); } - public expressionStatement( - node: ts.ExpressionStatement, - context: AstRenderer, - ): OTree { + public expressionStatement(node: ts.ExpressionStatement, context: AstRenderer): OTree { return new OTree([context.convert(node.expression)], [], { canBreakLine: true, }); } - public token( - node: ts.Token, - context: AstRenderer, - ): OTree { + public token(node: ts.Token, context: AstRenderer): OTree { return new OTree([context.textOf(node)]); } @@ -180,13 +129,8 @@ export abstract class DefaultVisitor implements AstHandler { * - It's a struct (render as known struct) * - It's not a struct (render as key-value map) */ - public objectLiteralExpression( - node: ts.ObjectLiteralExpression, - context: AstRenderer, - ): OTree { - const type = typeWithoutUndefinedUnion( - context.inferredTypeOfExpression(node), - ); + public objectLiteralExpression(node: ts.ObjectLiteralExpression, context: AstRenderer): OTree { + const type = typeWithoutUndefinedUnion(context.inferredTypeOfExpression(node)); const isUnknownType = !type || !type.symbol; const isKnownStruct = type && isStructType(type); @@ -197,17 +141,10 @@ export abstract class DefaultVisitor implements AstHandler { if (isKnownStruct) { return this.knownStructObjectLiteralExpression(node, type!, context); } - return this.keyValueObjectLiteralExpression( - node, - type && mapElementType(type, context), - context, - ); + return this.keyValueObjectLiteralExpression(node, type && mapElementType(type, context), context); } - public unknownTypeObjectLiteralExpression( - node: ts.ObjectLiteralExpression, - context: AstRenderer, - ): OTree { + public unknownTypeObjectLiteralExpression(node: ts.ObjectLiteralExpression, context: AstRenderer): OTree { return this.notImplemented(node, context); } @@ -229,142 +166,85 @@ export abstract class DefaultVisitor implements AstHandler { public newExpression(node: ts.NewExpression, context: AstRenderer): OTree { return new OTree( - [ - 'new ', - context.convert(node.expression), - '(', - this.argumentList(node.arguments, context), - ')', - ], + ['new ', context.convert(node.expression), '(', this.argumentList(node.arguments, context), ')'], [], { canBreakLine: true }, ); } - public propertyAssignment( - node: ts.PropertyAssignment, - context: AstRenderer, - ): OTree { + public propertyAssignment(node: ts.PropertyAssignment, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public variableStatement( - node: ts.VariableStatement, - context: AstRenderer, - ): OTree { + public variableStatement(node: ts.VariableStatement, context: AstRenderer): OTree { return new OTree([context.convert(node.declarationList)], [], { canBreakLine: true, }); } - public variableDeclarationList( - node: ts.VariableDeclarationList, - context: AstRenderer, - ): OTree { + public variableDeclarationList(node: ts.VariableDeclarationList, context: AstRenderer): OTree { return new OTree([], context.convertAll(node.declarations)); } - public variableDeclaration( - node: ts.VariableDeclaration, - context: AstRenderer, - ): OTree { + public variableDeclaration(node: ts.VariableDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - context: AstRenderer, - ): OTree { + public arrayLiteralExpression(node: ts.ArrayLiteralExpression, context: AstRenderer): OTree { return new OTree(['['], context.convertAll(node.elements), { separator: ', ', suffix: ']', }); } - public shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - context: AstRenderer, - ): OTree { + public shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public forOfStatement( - node: ts.ForOfStatement, - context: AstRenderer, - ): OTree { + public forOfStatement(node: ts.ForOfStatement, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public classDeclaration( - node: ts.ClassDeclaration, - context: AstRenderer, - ): OTree { + public classDeclaration(node: ts.ClassDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public constructorDeclaration( - node: ts.ConstructorDeclaration, - context: AstRenderer, - ): OTree { + public constructorDeclaration(node: ts.ConstructorDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public propertyDeclaration( - node: ts.PropertyDeclaration, - context: AstRenderer, - ): OTree { + public propertyDeclaration(node: ts.PropertyDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public computedPropertyName( - node: ts.Expression, - context: AstRenderer, - ): OTree { + public computedPropertyName(node: ts.Expression, context: AstRenderer): OTree { return context.convert(node); } - public methodDeclaration( - node: ts.MethodDeclaration, - context: AstRenderer, - ): OTree { + public methodDeclaration(node: ts.MethodDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public interfaceDeclaration( - node: ts.InterfaceDeclaration, - context: AstRenderer, - ): OTree { + public interfaceDeclaration(node: ts.InterfaceDeclaration, context: AstRenderer): OTree { if (isStructInterface(context.textOf(node.name))) { return this.structInterfaceDeclaration(node, context); } return this.regularInterfaceDeclaration(node, context); } - public structInterfaceDeclaration( - node: ts.InterfaceDeclaration, - context: AstRenderer, - ): OTree { + public structInterfaceDeclaration(node: ts.InterfaceDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public regularInterfaceDeclaration( - node: ts.InterfaceDeclaration, - context: AstRenderer, - ): OTree { + public regularInterfaceDeclaration(node: ts.InterfaceDeclaration, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public propertySignature( - node: ts.PropertySignature, - context: AstRenderer, - ): OTree { + public propertySignature(node: ts.PropertySignature, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public methodSignature( - node: ts.MethodSignature, - context: AstRenderer, - ): OTree { + public methodSignature(node: ts.MethodSignature, context: AstRenderer): OTree { return this.notImplemented(node, context); } @@ -376,46 +256,28 @@ export abstract class DefaultVisitor implements AstHandler { return this.notImplemented(node, context); } - public spreadAssignment( - node: ts.SpreadAssignment, - context: AstRenderer, - ): OTree { + public spreadAssignment(node: ts.SpreadAssignment, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public ellipsis( - _node: ts.SpreadElement | ts.SpreadAssignment, - _context: AstRenderer, - ): OTree { + public ellipsis(_node: ts.SpreadElement | ts.SpreadAssignment, _context: AstRenderer): OTree { return new OTree(['...']); } - public templateExpression( - node: ts.TemplateExpression, - context: AstRenderer, - ): OTree { + public templateExpression(node: ts.TemplateExpression, context: AstRenderer): OTree { return this.notImplemented(node, context); } - public nonNullExpression( - node: ts.NonNullExpression, - context: AstRenderer, - ): OTree { + public nonNullExpression(node: ts.NonNullExpression, context: AstRenderer): OTree { // We default we drop the non-null assertion return context.convert(node.expression); } - public parenthesizedExpression( - node: ts.ParenthesizedExpression, - context: AstRenderer, - ): OTree { + public parenthesizedExpression(node: ts.ParenthesizedExpression, context: AstRenderer): OTree { return new OTree(['(', context.convert(node.expression), ')']); } - public maskingVoidExpression( - node: ts.VoidExpression, - context: AstRenderer, - ): OTree { + public maskingVoidExpression(node: ts.VoidExpression, context: AstRenderer): OTree { // Don't render anything by default when nodes are masked const arg = voidExpressionString(node); if (arg === 'block') { @@ -435,10 +297,7 @@ export abstract class DefaultVisitor implements AstHandler { return NO_SYNTAX; } - protected argumentList( - args: readonly ts.Node[] | undefined, - context: AstRenderer, - ): OTree { + protected argumentList(args: readonly ts.Node[] | undefined, context: AstRenderer): OTree { return new OTree([], args ? context.convertAll(args) : [], { separator: ', ', }); diff --git a/packages/jsii-rosetta/lib/languages/java.ts b/packages/jsii-rosetta/lib/languages/java.ts index 2ada081a38..26e7cd0dc1 100644 --- a/packages/jsii-rosetta/lib/languages/java.ts +++ b/packages/jsii-rosetta/lib/languages/java.ts @@ -5,19 +5,9 @@ import { jsiiTargetParam } from '../jsii/packages'; import { TargetLanguage } from '../languages/target-language'; import { OTree, NO_SYNTAX } from '../o-tree'; import { AstRenderer } from '../renderer'; -import { - isReadOnly, - matchAst, - nodeOfType, - quoteStringLiteral, - visibility, -} from '../typescript/ast-utils'; +import { isReadOnly, matchAst, nodeOfType, quoteStringLiteral, visibility } from '../typescript/ast-utils'; import { ImportStatement } from '../typescript/imports'; -import { - builtInTypeName, - mapElementType, - typeWithoutUndefinedUnion, -} from '../typescript/types'; +import { builtInTypeName, mapElementType, typeWithoutUndefinedUnion } from '../typescript/types'; import { DefaultVisitor } from './default'; interface JavaContext { @@ -106,10 +96,7 @@ export class JavaVisitor extends DefaultVisitor { public readonly language = TargetLanguage.JAVA; public readonly defaultContext = {}; - public mergeContext( - old: JavaContext, - update: Partial, - ): JavaContext { + public mergeContext(old: JavaContext, update: Partial): JavaContext { return Object.assign({}, old, update); } @@ -120,24 +107,16 @@ export class JavaVisitor extends DefaultVisitor { } return new OTree( [], - importStatement.imports.elements.map( - (importEl) => `import ${namespace}.${importEl.sourceName};`, - ), + importStatement.imports.elements.map((importEl) => `import ${namespace}.${importEl.sourceName};`), { canBreakLine: true, separator: '\n' }, ); } - public classDeclaration( - node: ts.ClassDeclaration, - renderer: JavaRenderer, - ): OTree { + public classDeclaration(node: ts.ClassDeclaration, renderer: JavaRenderer): OTree { return this.renderClassDeclaration(node, renderer); } - public structInterfaceDeclaration( - node: ts.InterfaceDeclaration, - renderer: JavaRenderer, - ): OTree { + public structInterfaceDeclaration(node: ts.InterfaceDeclaration, renderer: JavaRenderer): OTree { // Render structs as simple Java classes with getters, and setters that return `this`. // This is a compromise between brevity // (rendering a full inner static Builder class, like JSII uses, would be quite verbose) @@ -148,19 +127,13 @@ export class JavaVisitor extends DefaultVisitor { return this.renderClassDeclaration(node, renderer); } - public regularInterfaceDeclaration( - node: ts.InterfaceDeclaration, - renderer: JavaRenderer, - ): OTree { + public regularInterfaceDeclaration(node: ts.InterfaceDeclaration, renderer: JavaRenderer): OTree { return new OTree( [ 'public ', 'interface ', renderer.convert(node.name), - ...this.typeHeritage( - node, - renderer.updateContext({ ignorePropertyPrefix: true }), - ), + ...this.typeHeritage(node, renderer.updateContext({ ignorePropertyPrefix: true })), ' {', ], renderer @@ -176,10 +149,7 @@ export class JavaVisitor extends DefaultVisitor { ); } - public propertySignature( - node: ts.PropertySignature, - renderer: JavaRenderer, - ): OTree { + public propertySignature(node: ts.PropertySignature, renderer: JavaRenderer): OTree { const propertyType = this.renderTypeNode(node.type, renderer, 'Object'); const propertyName = renderer.convert(node.name); @@ -199,11 +169,7 @@ export class JavaVisitor extends DefaultVisitor { propertyType, ' ', `get${capitalize(renderer.textOf(node.name))}()${blockSep}`, - isClass - ? this.renderBlock([ - new OTree(['\n'], ['return this.', propertyName, ';']), - ]) - : NO_SYNTAX, + isClass ? this.renderBlock([new OTree(['\n'], ['return this.', propertyName, ';'])]) : NO_SYNTAX, ], { canBreakLine: true, @@ -217,9 +183,7 @@ export class JavaVisitor extends DefaultVisitor { [], [ isClass ? 'public ' : NO_SYNTAX, - renderer.convert( - renderer.currentContext.insideTypeDeclaration!.typeName, - ), + renderer.convert(renderer.currentContext.insideTypeDeclaration!.typeName), ' ', propertyName, // don't prefix the setter with `set` - makes it more aligned with JSII builders '(', @@ -229,10 +193,7 @@ export class JavaVisitor extends DefaultVisitor { `)${blockSep}`, isClass ? this.renderBlock([ - new OTree( - ['\n'], - ['this.', propertyName, ' = ', propertyName, ';'], - ), + new OTree(['\n'], ['this.', propertyName, ' = ', propertyName, ';']), new OTree(['\n'], ['return this;']), ]) : NO_SYNTAX, @@ -249,10 +210,7 @@ export class JavaVisitor extends DefaultVisitor { }); } - public propertyDeclaration( - node: ts.PropertyDeclaration, - renderer: JavaRenderer, - ): OTree { + public propertyDeclaration(node: ts.PropertyDeclaration, renderer: JavaRenderer): OTree { const vis = visibility(node); return new OTree( @@ -272,46 +230,19 @@ export class JavaVisitor extends DefaultVisitor { ); } - public constructorDeclaration( - node: ts.ConstructorDeclaration, - renderer: JavaRenderer, - ): OTree { - return this.renderProcedure( - node, - renderer, - renderer.currentContext.insideTypeDeclaration!.typeName, - undefined, - ); + public constructorDeclaration(node: ts.ConstructorDeclaration, renderer: JavaRenderer): OTree { + return this.renderProcedure(node, renderer, renderer.currentContext.insideTypeDeclaration!.typeName, undefined); } - public methodDeclaration( - node: ts.MethodDeclaration, - renderer: JavaRenderer, - ): OTree { - return this.renderProcedure( - node, - renderer, - node.name, - this.renderTypeNode(node.type, renderer, 'void'), - ); + public methodDeclaration(node: ts.MethodDeclaration, renderer: JavaRenderer): OTree { + return this.renderProcedure(node, renderer, node.name, this.renderTypeNode(node.type, renderer, 'void')); } - public functionDeclaration( - node: ts.FunctionDeclaration, - renderer: JavaRenderer, - ): OTree { - return this.renderProcedure( - node, - renderer, - node.name, - this.renderTypeNode(node.type, renderer, 'void'), - ); + public functionDeclaration(node: ts.FunctionDeclaration, renderer: JavaRenderer): OTree { + return this.renderProcedure(node, renderer, node.name, this.renderTypeNode(node.type, renderer, 'void')); } - public methodSignature( - node: ts.MethodSignature, - renderer: JavaRenderer, - ): OTree { + public methodSignature(node: ts.MethodSignature, renderer: JavaRenderer): OTree { return new OTree( [ this.renderTypeNode(node.type, renderer, 'void'), @@ -328,41 +259,27 @@ export class JavaVisitor extends DefaultVisitor { ); } - public parameterDeclaration( - node: ts.ParameterDeclaration, - renderer: JavaRenderer, - ): OTree { - return new OTree([ - this.renderTypeNode(node.type, renderer), - ' ', - renderer.convert(node.name), - ]); + public parameterDeclaration(node: ts.ParameterDeclaration, renderer: JavaRenderer): OTree { + return new OTree([this.renderTypeNode(node.type, renderer), ' ', renderer.convert(node.name)]); } public block(node: ts.Block, renderer: JavaRenderer): OTree { return this.renderBlock(renderer.convertAll(node.statements)); } - public variableDeclaration( - node: ts.VariableDeclaration, - renderer: JavaRenderer, - ): OTree { + public variableDeclaration(node: ts.VariableDeclaration, renderer: JavaRenderer): OTree { const type = (node.type && renderer.typeOfType(node.type)) || (node.initializer && renderer.typeOfExpression(node.initializer)); - const renderedType = type - ? this.renderType(node, type, renderer, 'Object') - : 'Object'; + const renderedType = type ? this.renderType(node, type, renderer, 'Object') : 'Object'; return new OTree( [ renderedType, ' ', renderer.convert(node.name), - ...(node.initializer - ? [' = ', renderer.convert(node.initializer)] - : []), + ...(node.initializer ? [' = ', renderer.convert(node.initializer)] : []), ';', ], [], @@ -372,14 +289,9 @@ export class JavaVisitor extends DefaultVisitor { ); } - public expressionStatement( - node: ts.ExpressionStatement, - renderer: JavaRenderer, - ): OTree { + public expressionStatement(node: ts.ExpressionStatement, renderer: JavaRenderer): OTree { const inner = renderer.convert(node.expression); - return inner.isEmpty - ? inner - : new OTree([inner, ';'], [], { canBreakLine: true }); + return inner.isEmpty ? inner : new OTree([inner, ';'], [], { canBreakLine: true }); } public ifStatement(node: ts.IfStatement, renderer: JavaRenderer): OTree { @@ -404,32 +316,20 @@ export class JavaVisitor extends DefaultVisitor { : ifStmt; } - public forOfStatement( - node: ts.ForOfStatement, - renderer: JavaRenderer, - ): OTree { + public forOfStatement(node: ts.ForOfStatement, renderer: JavaRenderer): OTree { // This is what a "for (const x of ...)" looks like in the AST let variableName = '???'; matchAst( node.initializer, - nodeOfType( - ts.SyntaxKind.VariableDeclarationList, - nodeOfType('var', ts.SyntaxKind.VariableDeclaration), - ), + nodeOfType(ts.SyntaxKind.VariableDeclarationList, nodeOfType('var', ts.SyntaxKind.VariableDeclaration)), (bindings) => { variableName = renderer.textOf(bindings.var.name); }, ); return new OTree( - [ - 'for (Object ', - variableName, - ' : ', - renderer.convert(node.expression), - ') ', - ], + ['for (Object ', variableName, ' : ', renderer.convert(node.expression), ') '], [renderer.convert(node.statement)], { canBreakLine: true, @@ -437,23 +337,15 @@ export class JavaVisitor extends DefaultVisitor { ); } - public printStatement( - args: ts.NodeArray, - renderer: JavaRenderer, - ) { + public printStatement(args: ts.NodeArray, renderer: JavaRenderer) { return new OTree([ 'System.out.println(', - args.length === 1 - ? renderer.convert(args[0]) - : new OTree([], renderer.convertAll(args), { separator: ' + ' }), + args.length === 1 ? renderer.convert(args[0]) : new OTree([], renderer.convertAll(args), { separator: ' + ' }), ')', ]); } - public templateExpression( - node: ts.TemplateExpression, - renderer: JavaRenderer, - ): OTree { + public templateExpression(node: ts.TemplateExpression, renderer: JavaRenderer): OTree { let template = ''; const parameters = new Array(); @@ -485,27 +377,16 @@ export class JavaVisitor extends DefaultVisitor { `"${quoteStringLiteral(template) // Java does not have multiline string literals, so we must replace literal newlines with %n .replace(/\n/g, '%n')}"`, - ...parameters.reduce( - (list, element) => list.concat(', ', element), - new Array(), - ), + ...parameters.reduce((list, element) => list.concat(', ', element), new Array()), ')', ]); } public asExpression(node: ts.AsExpression, renderer: JavaRenderer): OTree { - return new OTree([ - '(', - this.renderTypeNode(node.type, renderer, 'Object'), - ')', - renderer.convert(node.expression), - ]); + return new OTree(['(', this.renderTypeNode(node.type, renderer, 'Object'), ')', renderer.convert(node.expression)]); } - public arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - renderer: JavaRenderer, - ): OTree { + public arrayLiteralExpression(node: ts.ArrayLiteralExpression, renderer: JavaRenderer): OTree { return new OTree(['asList('], renderer.convertAll(node.elements), { separator: ', ', suffix: ')', @@ -513,14 +394,9 @@ export class JavaVisitor extends DefaultVisitor { }); } - public regularCallExpression( - node: ts.CallExpression, - renderer: JavaRenderer, - ): OTree { + public regularCallExpression(node: ts.CallExpression, renderer: JavaRenderer): OTree { return new OTree([ - renderer - .updateContext({ convertPropertyToGetter: false }) - .convert(node.expression), + renderer.updateContext({ convertPropertyToGetter: false }).convert(node.expression), '(', this.argumentList(node.arguments, renderer), ')', @@ -529,18 +405,13 @@ export class JavaVisitor extends DefaultVisitor { public newExpression(node: ts.NewExpression, renderer: JavaRenderer): OTree { const argsLength = node.arguments ? node.arguments.length : 0; - const lastArg = - argsLength > 0 ? node.arguments![argsLength - 1] : undefined; - const lastArgIsObjectLiteral = - lastArg && ts.isObjectLiteralExpression(lastArg); - const lastArgType = - lastArg && - typeWithoutUndefinedUnion(renderer.inferredTypeOfExpression(lastArg)); + const lastArg = argsLength > 0 ? node.arguments![argsLength - 1] : undefined; + const lastArgIsObjectLiteral = lastArg && ts.isObjectLiteralExpression(lastArg); + const lastArgType = lastArg && typeWithoutUndefinedUnion(renderer.inferredTypeOfExpression(lastArg)); // we only render the ClassName.Builder.create(...) expression // if the last argument is an object literal, and NOT a known struct // (in that case, it has its own creation method) - const renderBuilderInsteadOfNew = - lastArgIsObjectLiteral && (!lastArgType || !isStructType(lastArgType)); + const renderBuilderInsteadOfNew = lastArgIsObjectLiteral && (!lastArgType || !isStructType(lastArgType)); return new OTree( [], @@ -555,16 +426,12 @@ export class JavaVisitor extends DefaultVisitor { renderBuilderInsteadOfNew ? '.Builder.create' : undefined, '(', this.argumentList( - renderBuilderInsteadOfNew - ? node.arguments!.slice(0, argsLength - 1) - : node.arguments, + renderBuilderInsteadOfNew ? node.arguments!.slice(0, argsLength - 1) : node.arguments, renderer, ), ')', renderBuilderInsteadOfNew - ? renderer - .updateContext({ inNewExprWithObjectLiteralAsLastArg: true }) - .convert(lastArg) + ? renderer.updateContext({ inNewExprWithObjectLiteralAsLastArg: true }).convert(lastArg) : undefined, ], { @@ -573,10 +440,7 @@ export class JavaVisitor extends DefaultVisitor { ); } - public unknownTypeObjectLiteralExpression( - node: ts.ObjectLiteralExpression, - renderer: JavaRenderer, - ): OTree { + public unknownTypeObjectLiteralExpression(node: ts.ObjectLiteralExpression, renderer: JavaRenderer): OTree { return renderer.currentContext.inNewExprWithObjectLiteralAsLastArg ? this.renderObjectLiteralAsBuilder(node, renderer) : this.keyValueObjectLiteralExpression(node, undefined, renderer); @@ -587,17 +451,11 @@ export class JavaVisitor extends DefaultVisitor { _valueType: ts.Type | undefined, renderer: JavaRenderer, ): OTree { - return new OTree( - ['Map.of('], - renderer - .updateContext({ inKeyValueList: true }) - .convertAll(node.properties), - { - suffix: ')', - separator: ', ', - indent: 8, - }, - ); + return new OTree(['Map.of('], renderer.updateContext({ inKeyValueList: true }).convertAll(node.properties), { + suffix: ')', + separator: ', ', + indent: 8, + }); } public knownStructObjectLiteralExpression( @@ -605,53 +463,24 @@ export class JavaVisitor extends DefaultVisitor { structType: ts.Type, renderer: JavaRenderer, ): OTree { - return new OTree( - ['new ', structType.symbol.name, '()'], - [...renderer.convertAll(node.properties)], - { - indent: 8, - }, - ); + return new OTree(['new ', structType.symbol.name, '()'], [...renderer.convertAll(node.properties)], { + indent: 8, + }); } - public propertyAssignment( - node: ts.PropertyAssignment, - renderer: JavaRenderer, - ): OTree { + public propertyAssignment(node: ts.PropertyAssignment, renderer: JavaRenderer): OTree { return renderer.currentContext.inKeyValueList - ? this.singlePropertyInJavaScriptObjectLiteralToJavaMap( - node.name, - node.initializer, - renderer, - ) - : this.singlePropertyInJavaScriptObjectLiteralToFluentSetters( - node.name, - node.initializer, - renderer, - ); + ? this.singlePropertyInJavaScriptObjectLiteralToJavaMap(node.name, node.initializer, renderer) + : this.singlePropertyInJavaScriptObjectLiteralToFluentSetters(node.name, node.initializer, renderer); } - public shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - renderer: JavaRenderer, - ): OTree { + public shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, renderer: JavaRenderer): OTree { return renderer.currentContext.inKeyValueList - ? this.singlePropertyInJavaScriptObjectLiteralToJavaMap( - node.name, - node.name, - renderer, - ) - : this.singlePropertyInJavaScriptObjectLiteralToFluentSetters( - node.name, - node.name, - renderer, - ); + ? this.singlePropertyInJavaScriptObjectLiteralToJavaMap(node.name, node.name, renderer) + : this.singlePropertyInJavaScriptObjectLiteralToFluentSetters(node.name, node.name, renderer); } - public propertyAccessExpression( - node: ts.PropertyAccessExpression, - renderer: JavaRenderer, - ): OTree { + public propertyAccessExpression(node: ts.PropertyAccessExpression, renderer: JavaRenderer): OTree { const rightHandSide = renderer.convert(node.name); let parts: Array; @@ -680,10 +509,7 @@ export class JavaVisitor extends DefaultVisitor { return new OTree(parts); } - public stringLiteral( - node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, - renderer: JavaRenderer, - ): OTree { + public stringLiteral(node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, renderer: JavaRenderer): OTree { if (renderer.currentContext.stringLiteralAsIdentifier) { return this.identifier(node, renderer); } @@ -696,25 +522,15 @@ export class JavaVisitor extends DefaultVisitor { renderer: JavaRenderer, ): OTree { const nodeText = node.text; - return new OTree([ - renderer.currentContext.identifierAsString - ? JSON.stringify(nodeText) - : nodeText, - ]); + return new OTree([renderer.currentContext.identifierAsString ? JSON.stringify(nodeText) : nodeText]); } - private renderObjectLiteralAsBuilder( - node: ts.ObjectLiteralExpression, - renderer: JavaRenderer, - ): OTree { + private renderObjectLiteralAsBuilder(node: ts.ObjectLiteralExpression, renderer: JavaRenderer): OTree { return new OTree( [], [ ...renderer.convertAll(node.properties), - new OTree( - [renderer.mirrorNewlineBefore(node.properties[0])], - ['.build()'], - ), + new OTree([renderer.mirrorNewlineBefore(node.properties[0])], ['.build()']), ], { indent: 8, @@ -753,13 +569,9 @@ export class JavaVisitor extends DefaultVisitor { [], [ '.', - renderer - .updateContext({ stringLiteralAsIdentifier: true }) - .convert(name), + renderer.updateContext({ stringLiteralAsIdentifier: true }).convert(name), '(', - renderer - .updateContext({ inNewExprWithObjectLiteralAsLastArg: false }) - .convert(initializer), + renderer.updateContext({ inNewExprWithObjectLiteralAsLastArg: false }).convert(initializer), ')', ], { @@ -782,24 +594,16 @@ export class JavaVisitor extends DefaultVisitor { ); } - private renderClassDeclaration( - node: ts.ClassDeclaration | ts.InterfaceDeclaration, - renderer: JavaRenderer, - ) { + private renderClassDeclaration(node: ts.ClassDeclaration | ts.InterfaceDeclaration, renderer: JavaRenderer) { return new OTree( [ 'public ', 'class ', renderer.convert(node.name), - ...this.typeHeritage( - node, - renderer.updateContext({ ignorePropertyPrefix: true }), - ), + ...this.typeHeritage(node, renderer.updateContext({ ignorePropertyPrefix: true })), ' {', ], - renderer - .updateContext({ insideTypeDeclaration: { typeName: node.name } }) - .convertAll(node.members), + renderer.updateContext({ insideTypeDeclaration: { typeName: node.name } }).convertAll(node.members), { indent: 4, canBreakLine: true, @@ -813,18 +617,8 @@ export class JavaVisitor extends DefaultVisitor { renderer: JavaRenderer, ): Array { return [ - ...this.extractSuperTypes( - node, - renderer, - ts.SyntaxKind.ExtendsKeyword, - 'extends', - ), - ...this.extractSuperTypes( - node, - renderer, - ts.SyntaxKind.ImplementsKeyword, - 'implements', - ), + ...this.extractSuperTypes(node, renderer, ts.SyntaxKind.ExtendsKeyword, 'extends'), + ...this.extractSuperTypes(node, renderer, ts.SyntaxKind.ImplementsKeyword, 'implements'), ]; } @@ -834,22 +628,12 @@ export class JavaVisitor extends DefaultVisitor { heritageKeyword: ts.SyntaxKind, outputKeyword: string, ): Array { - const heritageClause = (node.heritageClauses ?? []).find( - (hc) => hc.token === heritageKeyword, - ); - const superTypes = heritageClause - ? heritageClause.types.map((t) => renderer.convert(t.expression)) - : []; - return superTypes.length > 0 - ? [` ${outputKeyword} `, new OTree([], superTypes, { separator: ', ' })] - : []; + const heritageClause = (node.heritageClauses ?? []).find((hc) => hc.token === heritageKeyword); + const superTypes = heritageClause ? heritageClause.types.map((t) => renderer.convert(t.expression)) : []; + return superTypes.length > 0 ? [` ${outputKeyword} `, new OTree([], superTypes, { separator: ', ' })] : []; } - private renderTypeNode( - typeNode: ts.TypeNode | undefined, - renderer: JavaRenderer, - fallback?: string, - ): string { + private renderTypeNode(typeNode: ts.TypeNode | undefined, renderer: JavaRenderer, fallback?: string): string { fallback = fallback ?? (typeNode @@ -860,20 +644,10 @@ export class JavaVisitor extends DefaultVisitor { return fallback; } - return this.renderType( - typeNode, - renderer.typeOfType(typeNode), - renderer, - fallback, - ); + return this.renderType(typeNode, renderer.typeOfType(typeNode), renderer, fallback); } - private renderType( - owningNode: ts.Node, - type: ts.Type, - renderer: JavaRenderer, - fallback: string, - ): string { + private renderType(owningNode: ts.Node, type: ts.Type, renderer: JavaRenderer, fallback: string): string { // this means the snippet didn't have enough info for the TypeScript compiler to figure out the type - // so, just render the fallback if ((type as any).intrinsicName === 'error') { @@ -911,9 +685,7 @@ export class JavaVisitor extends DefaultVisitor { switch (typeScriptBuiltInType) { case 'boolean': - return renderer.currentContext.requiresReferenceType - ? 'Boolean' - : 'boolean'; + return renderer.currentContext.requiresReferenceType ? 'Boolean' : 'boolean'; case 'number': return 'Number'; case 'string': @@ -926,10 +698,7 @@ export class JavaVisitor extends DefaultVisitor { } private renderProcedure( - node: - | ts.ConstructorDeclaration - | ts.MethodDeclaration - | ts.FunctionDeclaration, + node: ts.ConstructorDeclaration | ts.MethodDeclaration | ts.FunctionDeclaration, renderer: JavaRenderer, methodOrConstructorName: ts.Node | undefined, returnType: string | undefined, @@ -946,9 +715,7 @@ export class JavaVisitor extends DefaultVisitor { // parameters up to but excluding the current one const parametersUpToIth = node.parameters.slice(0, i); // how should the call to the next overload look - const callExpr = ts.isConstructorDeclaration(node) - ? 'this' - : renderer.convert(methodOrConstructorName); + const callExpr = ts.isConstructorDeclaration(node) ? 'this' : renderer.convert(methodOrConstructorName); overloads.push( this.renderOverload( @@ -961,12 +728,8 @@ export class JavaVisitor extends DefaultVisitor { new OTree( ['\n', callExpr, '('], [ - ...parametersUpToIth.map((param) => - renderer.convert(param.name), - ), - param.initializer - ? renderer.convert(param.initializer) - : 'null', + ...parametersUpToIth.map((param) => renderer.convert(param.name)), + param.initializer ? renderer.convert(param.initializer) : 'null', ], { separator: ', ', @@ -980,13 +743,7 @@ export class JavaVisitor extends DefaultVisitor { } // render the primary overload overloads.push( - this.renderOverload( - returnType, - renderer, - methodOrConstructorName, - node.parameters, - renderer.convert(node.body), - ), + this.renderOverload(returnType, renderer, methodOrConstructorName, node.parameters, renderer.convert(node.body)), ); return new OTree([], overloads, { @@ -999,9 +756,7 @@ export class JavaVisitor extends DefaultVisitor { returnType: string | undefined, renderer: JavaRenderer, methodOrConstructorName: ts.Node | undefined, - parameters: - | ts.ParameterDeclaration[] - | ts.NodeArray, + parameters: ts.ParameterDeclaration[] | ts.NodeArray, body: OTree, ): OTree { return new OTree( diff --git a/packages/jsii-rosetta/lib/languages/python.ts b/packages/jsii-rosetta/lib/languages/python.ts index 9e6749f406..94b4b494e2 100644 --- a/packages/jsii-rosetta/lib/languages/python.ts +++ b/packages/jsii-rosetta/lib/languages/python.ts @@ -1,11 +1,6 @@ import * as ts from 'typescript'; -import { - isStructType, - propertiesOfStruct, - StructProperty, - structPropertyAcceptsUndefined, -} from '../jsii/jsii-utils'; +import { isStructType, propertiesOfStruct, StructProperty, structPropertyAcceptsUndefined } from '../jsii/jsii-utils'; import { jsiiTargetParam } from '../jsii/packages'; import { TargetLanguage } from '../languages/target-language'; import { NO_SYNTAX, OTree, renderTree } from '../o-tree'; @@ -94,21 +89,12 @@ export class PythonVisitor extends DefaultVisitor { super(); } - public mergeContext( - old: PythonLanguageContext, - update: Partial, - ) { + public mergeContext(old: PythonLanguageContext, update: Partial) { return Object.assign({}, old, update); } - public commentRange( - comment: CommentSyntax, - _context: PythonVisitorContext, - ): OTree { - const commentText = stripCommentMarkers( - comment.text, - comment.kind === ts.SyntaxKind.MultiLineCommentTrivia, - ); + public commentRange(comment: CommentSyntax, _context: PythonVisitorContext): OTree { + const commentText = stripCommentMarkers(comment.text, comment.kind === ts.SyntaxKind.MultiLineCommentTrivia); const hashLines = commentText .split('\n') .map((l) => `# ${l}`) @@ -130,45 +116,29 @@ export class PythonVisitor extends DefaultVisitor { return rendered; } - public importStatement( - node: ImportStatement, - context: PythonVisitorContext, - ): OTree { + public importStatement(node: ImportStatement, context: PythonVisitorContext): OTree { const moduleName = this.convertModuleReference(node.packageName); if (node.imports.import === 'full') { - return new OTree( - [`import ${moduleName} as ${mangleIdentifier(node.imports.alias)}`], - [], - { - canBreakLine: true, - }, - ); + return new OTree([`import ${moduleName} as ${mangleIdentifier(node.imports.alias)}`], [], { + canBreakLine: true, + }); } if (node.imports.import === 'selective') { const imports = node.imports.elements.map((im) => im.alias - ? `${mangleIdentifier(im.sourceName)} as ${mangleIdentifier( - im.alias, - )}` + ? `${mangleIdentifier(im.sourceName)} as ${mangleIdentifier(im.alias)}` : mangleIdentifier(im.sourceName), ); - return new OTree( - [`from ${moduleName} import ${imports.join(', ')}`], - [], - { - canBreakLine: true, - }, - ); + return new OTree([`from ${moduleName} import ${imports.join(', ')}`], [], { + canBreakLine: true, + }); } return nimpl(node.node, context); } - public token( - node: ts.Token, - context: PythonVisitorContext, - ): OTree { + public token(node: ts.Token, context: PythonVisitorContext): OTree { const text = context.textOf(node); const mapped = TOKEN_REWRITES[text]; if (mapped) { @@ -189,9 +159,7 @@ export class PythonVisitor extends DefaultVisitor { ) { return new OTree( [], - propertiesOfStruct(explodedParameter.type, context).map( - (prop) => new OTree([prop.name, '=', prop.name]), - ), + propertiesOfStruct(explodedParameter.type, context).map((prop) => new OTree([prop.name, '=', prop.name])), { separator: ', ' }, ); } @@ -199,31 +167,19 @@ export class PythonVisitor extends DefaultVisitor { return new OTree([mangleIdentifier(originalIdentifier)]); } - public functionDeclaration( - node: ts.FunctionDeclaration, - context: PythonVisitorContext, - ): OTree { + public functionDeclaration(node: ts.FunctionDeclaration, context: PythonVisitorContext): OTree { return this.functionLike(node, context); } - public constructorDeclaration( - node: ts.ConstructorDeclaration, - context: PythonVisitorContext, - ): OTree { + public constructorDeclaration(node: ts.ConstructorDeclaration, context: PythonVisitorContext): OTree { return this.functionLike(node, context, { isConstructor: true }); } - public methodDeclaration( - node: ts.MethodDeclaration, - context: PythonVisitorContext, - ): OTree { + public methodDeclaration(node: ts.MethodDeclaration, context: PythonVisitorContext): OTree { return this.functionLike(node, context); } - public expressionStatement( - node: ts.ExpressionStatement, - context: PythonVisitorContext, - ): OTree { + public expressionStatement(node: ts.ExpressionStatement, context: PythonVisitorContext): OTree { const text = context.textOf(node); if (text === 'true') { return new OTree(['True']); @@ -241,34 +197,21 @@ export class PythonVisitor extends DefaultVisitor { context: PythonVisitorContext, opts: { isConstructor?: boolean } = {}, ): OTree { - const methodName = opts.isConstructor - ? '__init__' - : renderTree(context.convert(node.name)); + const methodName = opts.isConstructor ? '__init__' : renderTree(context.convert(node.name)); - const [paramDecls, explodedParameter] = this.convertFunctionCallParameters( - node.parameters, - context, - ); + const [paramDecls, explodedParameter] = this.convertFunctionCallParameters(node.parameters, context); const ret = new OTree( [ 'def ', methodName, '(', - new OTree( - [], - [context.currentContext.inClass ? 'self' : undefined, ...paramDecls], - { - separator: ', ', - }, - ), + new OTree([], [context.currentContext.inClass ? 'self' : undefined, ...paramDecls], { + separator: ', ', + }), '): ', ], - [ - context - .updateContext({ explodedParameter, currentMethodName: methodName }) - .convert(node.body), - ], + [context.updateContext({ explodedParameter, currentMethodName: methodName }).convert(node.body)], { canBreakLine: true, }, @@ -289,16 +232,10 @@ export class PythonVisitor extends DefaultVisitor { }); } - public regularCallExpression( - node: ts.CallExpression, - context: PythonVisitorContext, - ): OTree { + public regularCallExpression(node: ts.CallExpression, context: PythonVisitorContext): OTree { let expressionText: OTree | string = context.convert(node.expression); - if ( - matchAst(node.expression, nodeOfType(ts.SyntaxKind.SuperKeyword)) && - context.currentContext.currentMethodName - ) { + if (matchAst(node.expression, nodeOfType(ts.SyntaxKind.SuperKeyword)) && context.currentContext.currentMethodName) { expressionText = `super().${context.currentContext.currentMethodName}`; } @@ -311,9 +248,7 @@ export class PythonVisitor extends DefaultVisitor { this.convertFunctionCallArguments( node.arguments, context, - signature?.parameters?.map( - (p) => p.valueDeclaration as ts.ParameterDeclaration, - ), + signature?.parameters?.map((p) => p.valueDeclaration as ts.ParameterDeclaration), ), ')', ], @@ -322,10 +257,7 @@ export class PythonVisitor extends DefaultVisitor { ); } - public propertyAccessExpression( - node: ts.PropertyAccessExpression, - context: PythonVisitorContext, - ) { + public propertyAccessExpression(node: ts.PropertyAccessExpression, context: PythonVisitorContext) { const fullText = context.textOf(node); if (fullText in BUILTIN_FUNCTIONS) { return new OTree([BUILTIN_FUNCTIONS[fullText]]); @@ -335,27 +267,17 @@ export class PythonVisitor extends DefaultVisitor { // We might be in a context where we've exploded this struct into arguments, // in which case we will return just the accessed variable. - if ( - explodedParameter && - context.textOf(node.expression) === explodedParameter.variableName - ) { + if (explodedParameter && context.textOf(node.expression) === explodedParameter.variableName) { return context.convert(node.name); } return super.propertyAccessExpression(node, context); } - public parameterDeclaration( - node: ts.ParameterDeclaration, - context: PythonVisitorContext, - ): OTree { + public parameterDeclaration(node: ts.ParameterDeclaration, context: PythonVisitorContext): OTree { const type = node.type && context.typeOfType(node.type); - if ( - context.currentContext.tailPositionParameter && - type && - isStructType(type) - ) { + if (context.currentContext.tailPositionParameter && type && isStructType(type)) { // Return the parameter that we exploded so that we can use this information // while translating the body. if (context.currentContext.returnExplodedParameter) { @@ -366,11 +288,7 @@ export class PythonVisitor extends DefaultVisitor { } // Explode to fields - return new OTree( - [], - ['*', ...propertiesOfStruct(type, context).map(renderStructProperty)], - { separator: ', ' }, - ); + return new OTree([], ['*', ...propertiesOfStruct(type, context).map(renderStructProperty)], { separator: ', ' }); } const suffix = parameterAcceptsUndefined(node, type) ? '=None' : ''; @@ -383,15 +301,10 @@ export class PythonVisitor extends DefaultVisitor { } } - public ifStatement( - node: ts.IfStatement, - context: PythonVisitorContext, - ): OTree { - const ifStmt = new OTree( - ['if ', context.convert(node.expression), ': '], - [context.convert(node.thenStatement)], - { canBreakLine: true }, - ); + public ifStatement(node: ts.IfStatement, context: PythonVisitorContext): OTree { + const ifStmt = new OTree(['if ', context.convert(node.expression), ': '], [context.convert(node.thenStatement)], { + canBreakLine: true, + }); const elseStmt = node.elseStatement ? new OTree(['else: '], [context.convert(node.elseStatement)], { canBreakLine: true, @@ -406,36 +319,18 @@ export class PythonVisitor extends DefaultVisitor { : ifStmt; } - public unknownTypeObjectLiteralExpression( - node: ts.ObjectLiteralExpression, - context: PythonVisitorContext, - ): OTree { + public unknownTypeObjectLiteralExpression(node: ts.ObjectLiteralExpression, context: PythonVisitorContext): OTree { // Neutralize local modifiers if any for transforming further down. const downContext = context.updateContext({ tailPositionArgument: false, variadicArgument: false, }); - if ( - context.currentContext.tailPositionArgument && - !context.currentContext.variadicArgument - ) { + if (context.currentContext.tailPositionArgument && !context.currentContext.variadicArgument) { // Guess that it's a struct we can probably inline the kwargs for - return this.renderObjectLiteralExpression( - '', - '', - true, - node, - downContext, - ); + return this.renderObjectLiteralExpression('', '', true, node, downContext); } - return this.renderObjectLiteralExpression( - '{', - '}', - false, - node, - downContext, - ); + return this.renderObjectLiteralExpression('{', '}', false, node, downContext); } public knownStructObjectLiteralExpression( @@ -447,13 +342,7 @@ export class PythonVisitor extends DefaultVisitor { // We know it's a struct we can DEFINITELY inline the args for return this.renderObjectLiteralExpression('', '', true, node, context); } - return this.renderObjectLiteralExpression( - `${structType.symbol.name}(`, - ')', - true, - node, - context, - ); + return this.renderObjectLiteralExpression(`${structType.symbol.name}(`, ')', true, node, context); } public keyValueObjectLiteralExpression( @@ -471,23 +360,14 @@ export class PythonVisitor extends DefaultVisitor { node: ts.ObjectLiteralExpression, context: PythonVisitorContext, ): OTree { - return new OTree( - [prefix], - context - .updateContext({ renderObjectLiteralAsKeywords }) - .convertAll(node.properties), - { - suffix: context.mirrorNewlineBefore(node.properties[0], suffix), - separator: ', ', - indent: 4, - }, - ); + return new OTree([prefix], context.updateContext({ renderObjectLiteralAsKeywords }).convertAll(node.properties), { + suffix: context.mirrorNewlineBefore(node.properties[0], suffix), + separator: ', ', + indent: 4, + }); } - public arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - context: PythonVisitorContext, - ): OTree { + public arrayLiteralExpression(node: ts.ArrayLiteralExpression, context: PythonVisitorContext): OTree { return new OTree(['['], context.convertAll(node.elements), { suffix: context.mirrorNewlineBefore(node.elements[0], ']'), separator: ', ', @@ -495,50 +375,29 @@ export class PythonVisitor extends DefaultVisitor { }); } - public propertyAssignment( - node: ts.PropertyAssignment, - context: PythonVisitorContext, - ): OTree { - const mid = context.currentContext.renderObjectLiteralAsKeywords - ? '=' - : ': '; + public propertyAssignment(node: ts.PropertyAssignment, context: PythonVisitorContext): OTree { + const mid = context.currentContext.renderObjectLiteralAsKeywords ? '=' : ': '; // node.name is either an identifier or a string literal. The string literal // needs to be converted differently. let name = context.convert(node.name); - matchAst( - node.name, - nodeOfType('stringLiteral', ts.SyntaxKind.StringLiteral), - (captured) => { - name = new OTree([mangleIdentifier(captured.stringLiteral.text)]); - }, - ); + matchAst(node.name, nodeOfType('stringLiteral', ts.SyntaxKind.StringLiteral), (captured) => { + name = new OTree([mangleIdentifier(captured.stringLiteral.text)]); + }); // If this isn't a computed property, we must quote the key (unless it's rendered as a keyword) - if ( - !context.currentContext.renderObjectLiteralAsKeywords && - !ts.isComputedPropertyName(node.name) - ) { + if (!context.currentContext.renderObjectLiteralAsKeywords && !ts.isComputedPropertyName(node.name)) { name = new OTree(['"', name, '"']); } return new OTree( - [ - name, - mid, - context - .updateContext({ tailPositionArgument: false }) - .convert(node.initializer), - ], + [name, mid, context.updateContext({ tailPositionArgument: false }).convert(node.initializer)], [], { canBreakLine: true }, ); } - public shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - context: PythonVisitorContext, - ): OTree { + public shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, context: PythonVisitorContext): OTree { let before = '"'; let mid = '": '; @@ -547,57 +406,34 @@ export class PythonVisitor extends DefaultVisitor { mid = '='; } - return new OTree( - [before, context.convert(node.name), mid, context.convert(node.name)], - [], - { canBreakLine: true }, - ); + return new OTree([before, context.convert(node.name), mid, context.convert(node.name)], [], { canBreakLine: true }); } - public newExpression( - node: ts.NewExpression, - context: PythonVisitorContext, - ): OTree { + public newExpression(node: ts.NewExpression, context: PythonVisitorContext): OTree { return new OTree( - [ - context.convert(node.expression), - '(', - this.convertFunctionCallArguments(node.arguments, context), - ')', - ], + [context.convert(node.expression), '(', this.convertFunctionCallArguments(node.arguments, context), ')'], [], { canBreakLine: true }, ); } - public variableDeclaration( - node: ts.VariableDeclaration, - context: PythonVisitorContext, - ): OTree { - return new OTree( - [context.convert(node.name), ' = ', context.convert(node.initializer)], - [], - { canBreakLine: true }, - ); + public variableDeclaration(node: ts.VariableDeclaration, context: PythonVisitorContext): OTree { + return new OTree([context.convert(node.name), ' = ', context.convert(node.initializer)], [], { + canBreakLine: true, + }); } public thisKeyword() { return new OTree(['self']); } - public forOfStatement( - node: ts.ForOfStatement, - context: PythonVisitorContext, - ): OTree { + public forOfStatement(node: ts.ForOfStatement, context: PythonVisitorContext): OTree { // This is what a "for (const x of ...)" looks like in the AST let variableName = '???'; matchAst( node.initializer, - nodeOfType( - ts.SyntaxKind.VariableDeclarationList, - nodeOfType('var', ts.SyntaxKind.VariableDeclaration), - ), + nodeOfType(ts.SyntaxKind.VariableDeclarationList, nodeOfType('var', ts.SyntaxKind.VariableDeclaration)), (bindings) => { variableName = mangleIdentifier(context.textOf(bindings.var.name)); }, @@ -610,18 +446,13 @@ export class PythonVisitor extends DefaultVisitor { ); } - public classDeclaration( - node: ts.ClassDeclaration, - context: PythonVisitorContext, - ): OTree { - const heritage = flat( - Array.from(node.heritageClauses ?? []).map((h) => Array.from(h.types)), - ).map((t) => context.convert(t.expression)); + public classDeclaration(node: ts.ClassDeclaration, context: PythonVisitorContext): OTree { + const heritage = flat(Array.from(node.heritageClauses ?? []).map((h) => Array.from(h.types))).map((t) => + context.convert(t.expression), + ); const hasHeritage = heritage.length > 0; - const members = context - .updateContext({ inClass: true }) - .convertAll(node.members); + const members = context.updateContext({ inClass: true }).convertAll(node.members); if (members.length === 0) { members.push(new OTree(['\npass'], [])); } @@ -645,22 +476,11 @@ export class PythonVisitor extends DefaultVisitor { return ret; } - public printStatement( - args: ts.NodeArray, - context: PythonVisitorContext, - ) { - return new OTree([ - 'print', - '(', - new OTree([], context.convertAll(args), { separator: ', ' }), - ')', - ]); + public printStatement(args: ts.NodeArray, context: PythonVisitorContext) { + return new OTree(['print', '(', new OTree([], context.convertAll(args), { separator: ', ' }), ')']); } - public propertyDeclaration( - _node: ts.PropertyDeclaration, - _context: PythonVisitorContext, - ): OTree { + public propertyDeclaration(_node: ts.PropertyDeclaration, _context: PythonVisitorContext): OTree { return new OTree([]); } @@ -670,34 +490,22 @@ export class PythonVisitor extends DefaultVisitor { * Best-effort, we remember the fields of struct interfaces and keep track of * them. Fortunately we can determine from the name whether what to do. */ - public interfaceDeclaration( - _node: ts.InterfaceDeclaration, - _context: PythonVisitorContext, - ): OTree { + public interfaceDeclaration(_node: ts.InterfaceDeclaration, _context: PythonVisitorContext): OTree { // Whatever we do, nothing here will have a representation return NO_SYNTAX; } - public propertySignature( - _node: ts.PropertySignature, - _context: PythonVisitorContext, - ): OTree { + public propertySignature(_node: ts.PropertySignature, _context: PythonVisitorContext): OTree { // Does not represent in Python return NO_SYNTAX; } - public methodSignature( - _node: ts.MethodSignature, - _context: PythonVisitorContext, - ): OTree { + public methodSignature(_node: ts.MethodSignature, _context: PythonVisitorContext): OTree { // Does not represent in Python return NO_SYNTAX; } - public asExpression( - node: ts.AsExpression, - context: PythonVisitorContext, - ): OTree { + public asExpression(node: ts.AsExpression, context: PythonVisitorContext): OTree { return context.convert(node.expression); } @@ -720,10 +528,7 @@ export class PythonVisitor extends DefaultVisitor { return new OTree([JSON.stringify(rawText)]); } - public templateExpression( - node: ts.TemplateExpression, - context: PythonVisitorContext, - ): OTree { + public templateExpression(node: ts.TemplateExpression, context: PythonVisitorContext): OTree { const parts = new Array(); if (node.head.rawText) { parts.push(quoteStringLiteral(node.head.rawText)); @@ -740,10 +545,7 @@ export class PythonVisitor extends DefaultVisitor { return new OTree([`f${quote}`, ...parts, quote]); } - public maskingVoidExpression( - node: ts.VoidExpression, - _context: PythonVisitorContext, - ): OTree { + public maskingVoidExpression(node: ts.VoidExpression, _context: PythonVisitorContext): OTree { const arg = voidExpressionString(node); if (arg === 'block') { return new OTree(['# ...'], [], { canBreakLine: true }); @@ -760,10 +562,7 @@ export class PythonVisitor extends DefaultVisitor { // Return that or some default-derived module name representation - return ( - resolvedPackage || - ref.replace(/^@/, '').replace(/\//g, '.').replace(/-/g, '_') - ); + return resolvedPackage || ref.replace(/^@/, '').replace(/\//g, '.').replace(/-/g, '_'); } /** @@ -819,10 +618,7 @@ export class PythonVisitor extends DefaultVisitor { } const converted = context.convertWithModifier(args, (ctx, _arg, index) => { - const decl = - parameterDeclarations?.[ - Math.min(index, parameterDeclarations.length - 1) - ]; + const decl = parameterDeclarations?.[Math.min(index, parameterDeclarations.length - 1)]; const variadicArgument = decl?.dotDotDotToken != null; const tailPositionArgument = index >= args.length - 1; @@ -839,10 +635,7 @@ function mangleIdentifier(originalIdentifier: string) { return originalIdentifier; } // Turn into snake-case - const cased = originalIdentifier.replace( - /[^A-Z][A-Z]/g, - (m) => `${m[0].substr(0, 1)}_${m.substr(1).toLowerCase()}`, - ); + const cased = originalIdentifier.replace(/[^A-Z][A-Z]/g, (m) => `${m[0].substr(0, 1)}_${m.substr(1).toLowerCase()}`); return IDENTIFIER_KEYWORDS.includes(cased) ? `${cased}_` : cased; } diff --git a/packages/jsii-rosetta/lib/languages/visualize.ts b/packages/jsii-rosetta/lib/languages/visualize.ts index 027b3b17ce..8eaa602a8b 100644 --- a/packages/jsii-rosetta/lib/languages/visualize.ts +++ b/packages/jsii-rosetta/lib/languages/visualize.ts @@ -26,24 +26,15 @@ export class VisualizeAstVisitor implements AstHandler { return new OTree(context.convertAll(node.statements)); } - public importStatement( - node: ImportStatement, - context: AstRenderer, - ): OTree { + public importStatement(node: ImportStatement, context: AstRenderer): OTree { return this.defaultNode('importStatement', node.node, context); } - public functionDeclaration( - node: ts.FunctionDeclaration, - children: AstRenderer, - ): OTree { + public functionDeclaration(node: ts.FunctionDeclaration, children: AstRenderer): OTree { return this.defaultNode('functionDeclaration', node, children); } - public stringLiteral( - node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, - children: AstRenderer, - ): OTree { + public stringLiteral(node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, children: AstRenderer): OTree { return this.defaultNode('stringLiteral', node, children); } @@ -55,24 +46,15 @@ export class VisualizeAstVisitor implements AstHandler { return this.defaultNode('block', node, children); } - public parameterDeclaration( - node: ts.ParameterDeclaration, - children: AstRenderer, - ): OTree { + public parameterDeclaration(node: ts.ParameterDeclaration, children: AstRenderer): OTree { return this.defaultNode('parameterDeclaration', node, children); } - public returnStatement( - node: ts.ReturnStatement, - children: AstRenderer, - ): OTree { + public returnStatement(node: ts.ReturnStatement, children: AstRenderer): OTree { return this.defaultNode('returnStatement', node, children); } - public binaryExpression( - node: ts.BinaryExpression, - children: AstRenderer, - ): OTree { + public binaryExpression(node: ts.BinaryExpression, children: AstRenderer): OTree { return this.defaultNode('binaryExpression', node, children); } @@ -80,221 +62,127 @@ export class VisualizeAstVisitor implements AstHandler { return this.defaultNode('ifStatement', node, context); } - public propertyAccessExpression( - node: ts.PropertyAccessExpression, - context: AstRenderer, - ): OTree { + public propertyAccessExpression(node: ts.PropertyAccessExpression, context: AstRenderer): OTree { return this.defaultNode('propertyAccessExpression', node, context); } - public callExpression( - node: ts.CallExpression, - context: AstRenderer, - ): OTree { + public callExpression(node: ts.CallExpression, context: AstRenderer): OTree { return this.defaultNode('callExpression', node, context); } - public expressionStatement( - node: ts.ExpressionStatement, - context: AstRenderer, - ): OTree { + public expressionStatement(node: ts.ExpressionStatement, context: AstRenderer): OTree { return this.defaultNode('expressionStatement', node, context); } - public token( - node: ts.Token, - context: AstRenderer, - ): OTree { + public token(node: ts.Token, context: AstRenderer): OTree { return this.defaultNode('token', node, context); } - public objectLiteralExpression( - node: ts.ObjectLiteralExpression, - context: AstRenderer, - ): OTree { + public objectLiteralExpression(node: ts.ObjectLiteralExpression, context: AstRenderer): OTree { return this.defaultNode('objectLiteralExpression', node, context); } - public newExpression( - node: ts.NewExpression, - context: AstRenderer, - ): OTree { + public newExpression(node: ts.NewExpression, context: AstRenderer): OTree { return this.defaultNode('newExpression', node, context); } - public propertyAssignment( - node: ts.PropertyAssignment, - context: AstRenderer, - ): OTree { + public propertyAssignment(node: ts.PropertyAssignment, context: AstRenderer): OTree { return this.defaultNode('propertyAssignment', node, context); } - public variableStatement( - node: ts.VariableStatement, - context: AstRenderer, - ): OTree { + public variableStatement(node: ts.VariableStatement, context: AstRenderer): OTree { return this.defaultNode('variableStatement', node, context); } - public variableDeclarationList( - node: ts.VariableDeclarationList, - context: AstRenderer, - ): OTree { + public variableDeclarationList(node: ts.VariableDeclarationList, context: AstRenderer): OTree { return this.defaultNode('variableDeclarationList', node, context); } - public variableDeclaration( - node: ts.VariableDeclaration, - context: AstRenderer, - ): OTree { + public variableDeclaration(node: ts.VariableDeclaration, context: AstRenderer): OTree { return this.defaultNode('variableDeclaration', node, context); } - public arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - context: AstRenderer, - ): OTree { + public arrayLiteralExpression(node: ts.ArrayLiteralExpression, context: AstRenderer): OTree { return this.defaultNode('arrayLiteralExpression', node, context); } - public shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - context: AstRenderer, - ): OTree { + public shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, context: AstRenderer): OTree { return this.defaultNode('shorthandPropertyAssignment', node, context); } - public forOfStatement( - node: ts.ForOfStatement, - context: AstRenderer, - ): OTree { + public forOfStatement(node: ts.ForOfStatement, context: AstRenderer): OTree { return this.defaultNode('forOfStatement', node, context); } - public classDeclaration( - node: ts.ClassDeclaration, - context: AstRenderer, - ): OTree { + public classDeclaration(node: ts.ClassDeclaration, context: AstRenderer): OTree { return this.defaultNode('classDeclaration', node, context); } - public constructorDeclaration( - node: ts.ConstructorDeclaration, - context: AstRenderer, - ): OTree { + public constructorDeclaration(node: ts.ConstructorDeclaration, context: AstRenderer): OTree { return this.defaultNode('constructorDeclaration', node, context); } - public propertyDeclaration( - node: ts.PropertyDeclaration, - context: AstRenderer, - ): OTree { + public propertyDeclaration(node: ts.PropertyDeclaration, context: AstRenderer): OTree { return this.defaultNode('propertyDeclaration', node, context); } - public computedPropertyName( - node: ts.Expression, - context: AstRenderer, - ): OTree { + public computedPropertyName(node: ts.Expression, context: AstRenderer): OTree { return this.defaultNode('computedPropertyName', node, context); } - public methodDeclaration( - node: ts.MethodDeclaration, - context: AstRenderer, - ): OTree { + public methodDeclaration(node: ts.MethodDeclaration, context: AstRenderer): OTree { return this.defaultNode('methodDeclaration', node, context); } - public interfaceDeclaration( - node: ts.InterfaceDeclaration, - context: AstRenderer, - ): OTree { + public interfaceDeclaration(node: ts.InterfaceDeclaration, context: AstRenderer): OTree { return this.defaultNode('interfaceDeclaration', node, context); } - public propertySignature( - node: ts.PropertySignature, - context: AstRenderer, - ): OTree { + public propertySignature(node: ts.PropertySignature, context: AstRenderer): OTree { return this.defaultNode('propertySignature', node, context); } - public methodSignature( - node: ts.MethodSignature, - context: AstRenderer, - ): OTree { + public methodSignature(node: ts.MethodSignature, context: AstRenderer): OTree { return this.defaultNode('methodSignature', node, context); } - public asExpression( - node: ts.AsExpression, - context: AstRenderer, - ): OTree { + public asExpression(node: ts.AsExpression, context: AstRenderer): OTree { return this.defaultNode('asExpression', node, context); } - public prefixUnaryExpression( - node: ts.PrefixUnaryExpression, - context: AstRenderer, - ): OTree { + public prefixUnaryExpression(node: ts.PrefixUnaryExpression, context: AstRenderer): OTree { return this.defaultNode('prefixUnaryExpression', node, context); } - public spreadElement( - node: ts.SpreadElement, - context: AstRenderer, - ): OTree { + public spreadElement(node: ts.SpreadElement, context: AstRenderer): OTree { return this.defaultNode('spreadElement', node, context); } - public spreadAssignment( - node: ts.SpreadAssignment, - context: AstRenderer, - ): OTree { + public spreadAssignment(node: ts.SpreadAssignment, context: AstRenderer): OTree { return this.defaultNode('spreadAssignment', node, context); } - public ellipsis( - node: ts.SpreadAssignment | ts.SpreadElement, - context: AstRenderer, - ): OTree { + public ellipsis(node: ts.SpreadAssignment | ts.SpreadElement, context: AstRenderer): OTree { return this.defaultNode('ellipsis', node, context); } - public templateExpression( - node: ts.TemplateExpression, - context: AstRenderer, - ): OTree { + public templateExpression(node: ts.TemplateExpression, context: AstRenderer): OTree { return this.defaultNode('templateExpression', node, context); } - public nonNullExpression( - node: ts.NonNullExpression, - context: AstRenderer, - ): OTree { + public nonNullExpression(node: ts.NonNullExpression, context: AstRenderer): OTree { return this.defaultNode('nonNullExpression', node, context); } - public parenthesizedExpression( - node: ts.ParenthesizedExpression, - context: AstRenderer, - ): OTree { + public parenthesizedExpression(node: ts.ParenthesizedExpression, context: AstRenderer): OTree { return this.defaultNode('parenthesizedExpression', node, context); } - public maskingVoidExpression( - node: ts.VoidExpression, - context: AstRenderer, - ): OTree { + public maskingVoidExpression(node: ts.VoidExpression, context: AstRenderer): OTree { return this.defaultNode('maskingVoidExpression', node, context); } - private defaultNode( - handlerName: string, - node: ts.Node, - context: AstRenderer, - ): OTree { + private defaultNode(handlerName: string, node: ts.Node, context: AstRenderer): OTree { return nimpl(node, context, { additionalInfo: this.includeHandlerNames ? handlerName : '', }); diff --git a/packages/jsii-rosetta/lib/logging.ts b/packages/jsii-rosetta/lib/logging.ts index 79c816d698..2ef1988733 100644 --- a/packages/jsii-rosetta/lib/logging.ts +++ b/packages/jsii-rosetta/lib/logging.ts @@ -32,8 +32,6 @@ export function debug(fmt: string, ...args: any[]) { function log(messageLevel: Level, fmt: string, ...args: any[]) { if (level >= messageLevel) { const levelName = Level[messageLevel]; - process.stderr.write( - `[jsii-rosetta] [${levelName}] ${util.format(fmt, ...args)}\n`, - ); + process.stderr.write(`[jsii-rosetta] [${levelName}] ${util.format(fmt, ...args)}\n`); } } diff --git a/packages/jsii-rosetta/lib/markdown/extract-snippets.ts b/packages/jsii-rosetta/lib/markdown/extract-snippets.ts index 8a7816a091..5f09699704 100644 --- a/packages/jsii-rosetta/lib/markdown/extract-snippets.ts +++ b/packages/jsii-rosetta/lib/markdown/extract-snippets.ts @@ -5,9 +5,7 @@ import { TypeScriptSnippet } from '../snippet'; import { ReplaceTypeScriptTransform } from './replace-typescript-transform'; import { CodeBlock } from './types'; -export type TypeScriptReplacer = ( - code: TypeScriptSnippet, -) => CodeBlock | undefined; +export type TypeScriptReplacer = (code: TypeScriptSnippet) => CodeBlock | undefined; export function extractTypescriptSnippetsFromMarkdown( markdown: string, diff --git a/packages/jsii-rosetta/lib/markdown/javadoc-renderer.ts b/packages/jsii-rosetta/lib/markdown/javadoc-renderer.ts index 0b91aaedaa..ffd7be8dae 100644 --- a/packages/jsii-rosetta/lib/markdown/javadoc-renderer.ts +++ b/packages/jsii-rosetta/lib/markdown/javadoc-renderer.ts @@ -2,13 +2,7 @@ import * as cm from 'commonmark'; import { makeJavaEscaper } from './escapes'; import { RendererContext } from './markdown'; -import { - MarkdownRenderer, - collapsePara, - para, - stripTrailingWhitespace, - stripPara, -} from './markdown-renderer'; +import { MarkdownRenderer, collapsePara, para, stripTrailingWhitespace, stripPara } from './markdown-renderer'; const ESCAPE = makeJavaEscaper(); @@ -37,9 +31,7 @@ export class JavaDocRenderer extends MarkdownRenderer { * tags with escaping of bad characters. */ public code_block(node: cm.Node, _context: RendererContext) { - return para( - `
\n${ESCAPE.text(node.literal)}
`, - ); + return para(`
\n${ESCAPE.text(node.literal)}
`); } public text(node: cm.Node, _context: RendererContext) { @@ -47,9 +39,7 @@ export class JavaDocRenderer extends MarkdownRenderer { } public link(node: cm.Node, context: RendererContext) { - return `
${context.content()}`; + return `${context.content()}`; } public document(_node: cm.Node, context: RendererContext) { @@ -71,9 +61,7 @@ export class JavaDocRenderer extends MarkdownRenderer { } public image(node: cm.Node, context: RendererContext) { - return `${ESCAPE.text2attr(context.content())}`; + return `${ESCAPE.text2attr(context.content())}`; } public emph(_node: cm.Node, context: RendererContext) { diff --git a/packages/jsii-rosetta/lib/markdown/markdown-renderer.ts b/packages/jsii-rosetta/lib/markdown/markdown-renderer.ts index 77aef8d652..f5a38db6fb 100644 --- a/packages/jsii-rosetta/lib/markdown/markdown-renderer.ts +++ b/packages/jsii-rosetta/lib/markdown/markdown-renderer.ts @@ -1,11 +1,6 @@ import * as cm from 'commonmark'; -import { - cmNodeChildren, - CommonMarkRenderer, - prefixLines, - RendererContext, -} from './markdown'; +import { cmNodeChildren, CommonMarkRenderer, prefixLines, RendererContext } from './markdown'; /** * A renderer that will render a CommonMark tree back to MarkDown @@ -79,9 +74,7 @@ export class MarkdownRenderer implements CommonMarkRenderer { const rendered = context.recurse(item); // Prefix the first line with a different text than subsequent lines - const prefixed = - firstLinePrefix + - prefixLines(hangingPrefix, rendered).substr(hangingPrefix.length); + const prefixed = firstLinePrefix + prefixLines(hangingPrefix, rendered).substr(hangingPrefix.length); items.push(prefixed); diff --git a/packages/jsii-rosetta/lib/markdown/markdown.ts b/packages/jsii-rosetta/lib/markdown/markdown.ts index 423d39cbb3..95c3fbdf67 100644 --- a/packages/jsii-rosetta/lib/markdown/markdown.ts +++ b/packages/jsii-rosetta/lib/markdown/markdown.ts @@ -1,10 +1,6 @@ import * as cm from 'commonmark'; -export function transformMarkdown( - source: string, - renderer: CommonMarkRenderer, - transform?: CommonMarkVisitor, -) { +export function transformMarkdown(source: string, renderer: CommonMarkRenderer, transform?: CommonMarkVisitor) { const parser = new cm.Parser(); const doc = parser.parse(source); if (transform) { @@ -42,10 +38,7 @@ export interface CommonMarkRenderer { custom_inline(node: cm.Node, context: RendererContext): string; } -export function renderCommonMarkTree( - node: cm.Node, - renderer: CommonMarkRenderer, -) { +export function renderCommonMarkTree(node: cm.Node, renderer: CommonMarkRenderer) { const context: RendererContext = { recurse(n: cm.Node): string { return renderCommonMarkTree(n, renderer); diff --git a/packages/jsii-rosetta/lib/markdown/replace-code-renderer.ts b/packages/jsii-rosetta/lib/markdown/replace-code-renderer.ts index 09031cee18..2ea2a97d8c 100644 --- a/packages/jsii-rosetta/lib/markdown/replace-code-renderer.ts +++ b/packages/jsii-rosetta/lib/markdown/replace-code-renderer.ts @@ -17,8 +17,7 @@ export class ReplaceCodeTransform implements CommonMarkVisitor { source: node.literal ?? '', }); node.info = ret.language; - node.literal = - ret.source + (!ret.source || ret.source.endsWith('\n') ? '' : '\n'); + node.literal = ret.source + (!ret.source || ret.source.endsWith('\n') ? '' : '\n'); } public block_quote(): void { diff --git a/packages/jsii-rosetta/lib/markdown/replace-typescript-transform.ts b/packages/jsii-rosetta/lib/markdown/replace-typescript-transform.ts index 8e00997944..113a835fe2 100644 --- a/packages/jsii-rosetta/lib/markdown/replace-typescript-transform.ts +++ b/packages/jsii-rosetta/lib/markdown/replace-typescript-transform.ts @@ -1,14 +1,8 @@ -import { - TypeScriptSnippet, - typeScriptSnippetFromSource, - parseKeyValueList, -} from '../snippet'; +import { TypeScriptSnippet, typeScriptSnippetFromSource, parseKeyValueList } from '../snippet'; import { ReplaceCodeTransform } from './replace-code-renderer'; import { CodeBlock } from './types'; -export type TypeScriptReplacer = ( - code: TypeScriptSnippet, -) => CodeBlock | undefined; +export type TypeScriptReplacer = (code: TypeScriptSnippet) => CodeBlock | undefined; /** * A specialization of ReplaceCodeTransform that maintains state about TypeScript snippets @@ -16,11 +10,7 @@ export type TypeScriptReplacer = ( export class ReplaceTypeScriptTransform extends ReplaceCodeTransform { private readonly wherePrefix: string; - public constructor( - wherePrefix: string, - strict: boolean, - replacer: TypeScriptReplacer, - ) { + public constructor(wherePrefix: string, strict: boolean, replacer: TypeScriptReplacer) { let count = 0; super((block) => { const languageParts = block.language ? block.language.split(' ') : []; diff --git a/packages/jsii-rosetta/lib/markdown/xml-comment-renderer.ts b/packages/jsii-rosetta/lib/markdown/xml-comment-renderer.ts index 5b58eba631..83e209b977 100644 --- a/packages/jsii-rosetta/lib/markdown/xml-comment-renderer.ts +++ b/packages/jsii-rosetta/lib/markdown/xml-comment-renderer.ts @@ -34,15 +34,11 @@ export class CSharpXmlCommentRenderer extends MarkdownRenderer { } public link(node: cm.Node, context: RendererContext) { - return `${context.content()}`; + return `${context.content()}`; } public image(node: cm.Node, context: RendererContext) { - return `${ESCAPE.text2attr(context.content())}`; + return `${ESCAPE.text2attr(context.content())}`; } public emph(_node: cm.Node, context: RendererContext) { diff --git a/packages/jsii-rosetta/lib/o-tree.ts b/packages/jsii-rosetta/lib/o-tree.ts index 583d3c92fa..0ea3b35bd2 100644 --- a/packages/jsii-rosetta/lib/o-tree.ts +++ b/packages/jsii-rosetta/lib/o-tree.ts @@ -50,9 +50,7 @@ export interface OTreeOptions { * can be rendered to an output sink. */ export class OTree implements OTree { - public static simplify( - xs: Array, - ): Array { + public static simplify(xs: Array): Array { return xs.filter(notUndefined).filter(notEmpty); } @@ -90,9 +88,7 @@ export class OTree implements OTree { sink.write(x); } - const popIndent = sink.requestIndentChange( - meVisible ? this.options.indent ?? 0 : 0, - ); + const popIndent = sink.requestIndentChange(meVisible ? this.options.indent ?? 0 : 0); let mark = sink.mark(); for (const child of this.children ?? []) { if (this.options.separator && mark.wroteNonWhitespaceSinceMark) { @@ -172,9 +168,7 @@ export class OTreeSink { return { get wroteNonWhitespaceSinceMark(): boolean { - return self.fragments - .slice(markIndex) - .some((s) => /[^\s]/.exec(s) != null); + return self.fragments.slice(markIndex).some((s) => /[^\s]/.exec(s) != null); }, }; } @@ -196,9 +190,7 @@ export class OTreeSink { public renderingForSpan(span?: Span): boolean { if (span && this.options.visibleSpans) { - this.rendering = this.options.visibleSpans.some((v) => - spanInside(span, v), - ); + this.rendering = this.options.visibleSpans.some((v) => spanInside(span, v)); } return this.rendering; } diff --git a/packages/jsii-rosetta/lib/renderer.ts b/packages/jsii-rosetta/lib/renderer.ts index 5b432f8a6b..24ccd1f77e 100644 --- a/packages/jsii-rosetta/lib/renderer.ts +++ b/packages/jsii-rosetta/lib/renderer.ts @@ -10,11 +10,7 @@ import { repeatNewlines, scanText, } from './typescript/ast-utils'; -import { - analyzeImportDeclaration, - analyzeImportEquals, - ImportStatement, -} from './typescript/imports'; +import { analyzeImportDeclaration, analyzeImportEquals, ImportStatement } from './typescript/imports'; /** * Render a TypeScript AST to some other representation (encoded in OTrees) @@ -41,10 +37,7 @@ export class AstRenderer { * Merge the new context with the current context and create a new Converter from it */ public updateContext(contextUpdate: Partial): AstRenderer { - const newContext = this.handler.mergeContext( - this.currentContext, - contextUpdate, - ); + const newContext = this.handler.mergeContext(this.currentContext, contextUpdate); // Use prototypal inheritance to create a version of 'this' in which only // 'currentContext' is updated. @@ -82,11 +75,7 @@ export class AstRenderer { public convertWithModifier( nodes: readonly ts.Node[], - makeContext: ( - context: this, - node: ts.Node, - index: number, - ) => AstRenderer, + makeContext: (context: this, node: ts.Node, index: number) => AstRenderer, ): OTree[] { const vis = assignVisibility(nodes); const result = new Array(); @@ -105,10 +94,7 @@ export class AstRenderer { * * Takes visibility into account. */ - public convertLastDifferently( - nodes: readonly ts.Node[], - lastContext: C, - ): OTree[] { + public convertLastDifferently(nodes: readonly ts.Node[], lastContext: C): OTree[] { const lastConverter = this.updateContext(lastContext); const convert = this.convert.bind(this); @@ -159,21 +145,14 @@ export class AstRenderer { * (Will return a map type for object literals) */ public typeOfExpression(node: ts.Expression): ts.Type { - return ( - this.typeChecker.getContextualType(node) ?? - this.typeChecker.getTypeAtLocation(node) - ); + return this.typeChecker.getContextualType(node) ?? this.typeChecker.getTypeAtLocation(node); } public typeOfType(node: ts.TypeNode): ts.Type { return this.typeChecker.getTypeFromTypeNode(node); } - public report( - node: ts.Node, - messageText: string, - category: ts.DiagnosticCategory = ts.DiagnosticCategory.Error, - ) { + public report(node: ts.Node, messageText: string, category: ts.DiagnosticCategory = ts.DiagnosticCategory.Error) { this.diagnostics.push({ category, code: 0, @@ -185,10 +164,7 @@ export class AstRenderer { }); } - public reportUnsupported( - node: ts.Node, - language: TargetLanguage | undefined, - ): void { + public reportUnsupported(node: ts.Node, language: TargetLanguage | undefined): void { const nodeKind = ts.SyntaxKind[node.kind]; // tslint:disable-next-line:max-line-length if (language) { @@ -209,28 +185,18 @@ export class AstRenderer { * * Used to mirror newline use between matchin brackets (such as { ... } and [ ... ]). */ - public mirrorNewlineBefore( - viz?: ts.Node, - suffix = '', - otherwise = '', - ): string { + public mirrorNewlineBefore(viz?: ts.Node, suffix = '', otherwise = ''): string { if (viz === undefined) { return suffix; } // Return a newline if the given node is preceded by newlines - const leadingRanges = scanText( - this.sourceFile.text, - viz.getFullStart(), - viz.getStart(this.sourceFile), - ); + const leadingRanges = scanText(this.sourceFile.text, viz.getFullStart(), viz.getStart(this.sourceFile)); const newlines = []; for (const range of leadingRanges) { if (range.type === 'other') { - newlines.push( - repeatNewlines(this.sourceFile.text.substring(range.pos, range.end)), - ); + newlines.push(repeatNewlines(this.sourceFile.text.substring(range.pos, range.end))); } } @@ -257,10 +223,7 @@ export class AstRenderer { return visitor.importStatement(analyzeImportEquals(tree, this), this); } if (ts.isImportDeclaration(tree)) { - return visitor.importStatement( - analyzeImportDeclaration(tree, this), - this, - ); + return visitor.importStatement(analyzeImportDeclaration(tree, this), this); } if (ts.isStringLiteral(tree) || ts.isNoSubstitutionTemplateLiteral(tree)) { return visitor.stringLiteral(tree, this); @@ -407,36 +370,21 @@ export class AstRenderer { */ private attachLeadingTrivia(node: ts.Node, transformed: OTree): OTree { // Add comments and leading whitespace - const leadingRanges = scanText( - this.sourceFile.text, - node.getFullStart(), - node.getStart(this.sourceFile), - ); + const leadingRanges = scanText(this.sourceFile.text, node.getFullStart(), node.getStart(this.sourceFile)); const precede: OTree[] = []; for (const range of leadingRanges) { let trivia: OTree | undefined = undefined; switch (range.type) { case 'other': - trivia = new OTree( - [ - repeatNewlines( - this.sourceFile.text.substring(range.pos, range.end), - ), - ], - [], - { - renderOnce: `ws-${range.pos}`, - }, - ); + trivia = new OTree([repeatNewlines(this.sourceFile.text.substring(range.pos, range.end))], [], { + renderOnce: `ws-${range.pos}`, + }); break; case 'linecomment': case 'blockcomment': trivia = this.handler.commentRange( - commentSyntaxFromCommentRange( - commentRangeFromTextRange(range), - this, - ), + commentSyntaxFromCommentRange(commentRangeFromTextRange(range), this), this, ); break; @@ -477,115 +425,51 @@ export interface AstHandler { sourceFile(node: ts.SourceFile, context: AstRenderer): OTree; commentRange(node: CommentSyntax, context: AstRenderer): OTree; importStatement(node: ImportStatement, context: AstRenderer): OTree; - stringLiteral( - node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, - children: AstRenderer, - ): OTree; - functionDeclaration( - node: ts.FunctionDeclaration, - children: AstRenderer, - ): OTree; + stringLiteral(node: ts.StringLiteral | ts.NoSubstitutionTemplateLiteral, children: AstRenderer): OTree; + functionDeclaration(node: ts.FunctionDeclaration, children: AstRenderer): OTree; identifier(node: ts.Identifier, children: AstRenderer): OTree; block(node: ts.Block, children: AstRenderer): OTree; - parameterDeclaration( - node: ts.ParameterDeclaration, - children: AstRenderer, - ): OTree; + parameterDeclaration(node: ts.ParameterDeclaration, children: AstRenderer): OTree; returnStatement(node: ts.ReturnStatement, context: AstRenderer): OTree; binaryExpression(node: ts.BinaryExpression, context: AstRenderer): OTree; ifStatement(node: ts.IfStatement, context: AstRenderer): OTree; - propertyAccessExpression( - node: ts.PropertyAccessExpression, - context: AstRenderer, - ): OTree; + propertyAccessExpression(node: ts.PropertyAccessExpression, context: AstRenderer): OTree; callExpression(node: ts.CallExpression, context: AstRenderer): OTree; - expressionStatement( - node: ts.ExpressionStatement, - context: AstRenderer, - ): OTree; - token( - node: ts.Token, - context: AstRenderer, - ): OTree; - objectLiteralExpression( - node: ts.ObjectLiteralExpression, - context: AstRenderer, - ): OTree; + expressionStatement(node: ts.ExpressionStatement, context: AstRenderer): OTree; + token(node: ts.Token, context: AstRenderer): OTree; + objectLiteralExpression(node: ts.ObjectLiteralExpression, context: AstRenderer): OTree; newExpression(node: ts.NewExpression, context: AstRenderer): OTree; - propertyAssignment( - node: ts.PropertyAssignment, - context: AstRenderer, - ): OTree; + propertyAssignment(node: ts.PropertyAssignment, context: AstRenderer): OTree; variableStatement(node: ts.VariableStatement, context: AstRenderer): OTree; - variableDeclarationList( - node: ts.VariableDeclarationList, - context: AstRenderer, - ): OTree; - variableDeclaration( - node: ts.VariableDeclaration, - context: AstRenderer, - ): OTree; + variableDeclarationList(node: ts.VariableDeclarationList, context: AstRenderer): OTree; + variableDeclaration(node: ts.VariableDeclaration, context: AstRenderer): OTree; jsDoc(node: ts.JSDoc, context: AstRenderer): OTree; - arrayLiteralExpression( - node: ts.ArrayLiteralExpression, - context: AstRenderer, - ): OTree; - shorthandPropertyAssignment( - node: ts.ShorthandPropertyAssignment, - context: AstRenderer, - ): OTree; + arrayLiteralExpression(node: ts.ArrayLiteralExpression, context: AstRenderer): OTree; + shorthandPropertyAssignment(node: ts.ShorthandPropertyAssignment, context: AstRenderer): OTree; forOfStatement(node: ts.ForOfStatement, context: AstRenderer): OTree; classDeclaration(node: ts.ClassDeclaration, context: AstRenderer): OTree; - constructorDeclaration( - node: ts.ConstructorDeclaration, - context: AstRenderer, - ): OTree; - propertyDeclaration( - node: ts.PropertyDeclaration, - context: AstRenderer, - ): OTree; + constructorDeclaration(node: ts.ConstructorDeclaration, context: AstRenderer): OTree; + propertyDeclaration(node: ts.PropertyDeclaration, context: AstRenderer): OTree; computedPropertyName(node: ts.Expression, context: AstRenderer): OTree; methodDeclaration(node: ts.MethodDeclaration, context: AstRenderer): OTree; - interfaceDeclaration( - node: ts.InterfaceDeclaration, - context: AstRenderer, - ): OTree; + interfaceDeclaration(node: ts.InterfaceDeclaration, context: AstRenderer): OTree; propertySignature(node: ts.PropertySignature, context: AstRenderer): OTree; methodSignature(node: ts.MethodSignature, context: AstRenderer): OTree; asExpression(node: ts.AsExpression, context: AstRenderer): OTree; - prefixUnaryExpression( - node: ts.PrefixUnaryExpression, - context: AstRenderer, - ): OTree; + prefixUnaryExpression(node: ts.PrefixUnaryExpression, context: AstRenderer): OTree; spreadElement(node: ts.SpreadElement, context: AstRenderer): OTree; spreadAssignment(node: ts.SpreadAssignment, context: AstRenderer): OTree; - templateExpression( - node: ts.TemplateExpression, - context: AstRenderer, - ): OTree; + templateExpression(node: ts.TemplateExpression, context: AstRenderer): OTree; nonNullExpression(node: ts.NonNullExpression, context: AstRenderer): OTree; - parenthesizedExpression( - node: ts.ParenthesizedExpression, - context: AstRenderer, - ): OTree; - maskingVoidExpression( - node: ts.VoidExpression, - context: AstRenderer, - ): OTree; + parenthesizedExpression(node: ts.ParenthesizedExpression, context: AstRenderer): OTree; + maskingVoidExpression(node: ts.VoidExpression, context: AstRenderer): OTree; // Not a node, called when we recognize a spread element/assignment that is only // '...' and nothing else. - ellipsis( - node: ts.SpreadElement | ts.SpreadAssignment, - context: AstRenderer, - ): OTree; + ellipsis(node: ts.SpreadElement | ts.SpreadAssignment, context: AstRenderer): OTree; } -export function nimpl( - node: ts.Node, - context: AstRenderer, - options: { additionalInfo?: string } = {}, -) { +export function nimpl(node: ts.Node, context: AstRenderer, options: { additionalInfo?: string } = {}) { const children = nodeChildren(node).map((c) => context.convert(c)); let syntaxKind = ts.SyntaxKind[node.kind]; @@ -600,16 +484,12 @@ export function nimpl( } parts.push(context.textOf(node)); - return new UnknownSyntax( - [parts.join(' ')], - children.length > 0 ? ['\n', ...children] : [], - { - indent: 2, - suffix: ')', - separator: '\n', - canBreakLine: true, - }, - ); + return new UnknownSyntax([parts.join(' ')], children.length > 0 ? ['\n', ...children] : [], { + indent: 2, + suffix: ')', + separator: '\n', + canBreakLine: true, + }); } export interface AstRendererOptions { @@ -672,10 +552,7 @@ export interface CommentSyntax { kind: ts.CommentKind; } -function commentSyntaxFromCommentRange( - rng: ts.CommentRange, - renderer: AstRenderer, -): CommentSyntax { +function commentSyntaxFromCommentRange(rng: ts.CommentRange, renderer: AstRenderer): CommentSyntax { return { hasTrailingNewLine: rng.hasTrailingNewLine, kind: rng.kind, diff --git a/packages/jsii-rosetta/lib/rosetta.ts b/packages/jsii-rosetta/lib/rosetta.ts index afb126107d..ff92f4d193 100644 --- a/packages/jsii-rosetta/lib/rosetta.ts +++ b/packages/jsii-rosetta/lib/rosetta.ts @@ -9,16 +9,8 @@ import { transformMarkdown } from './markdown/markdown'; import { MarkdownRenderer } from './markdown/markdown-renderer'; import { ReplaceTypeScriptTransform } from './markdown/replace-typescript-transform'; import { CodeBlock } from './markdown/types'; -import { - SnippetParameters, - TypeScriptSnippet, - updateParameters, -} from './snippet'; -import { - DEFAULT_TABLET_NAME, - LanguageTablet, - Translation, -} from './tablets/tablets'; +import { SnippetParameters, TypeScriptSnippet, updateParameters } from './snippet'; +import { DEFAULT_TABLET_NAME, LanguageTablet, Translation } from './tablets/tablets'; import { Translator } from './translate'; import { printDiagnostics } from './util'; @@ -72,9 +64,7 @@ export class Rosetta { public constructor(private readonly options: RosettaOptions = {}) { this.loose = !!options.loose; - this.translator = new Translator( - options.includeCompilerDiagnostics ?? false, - ); + this.translator = new Translator(options.includeCompilerDiagnostics ?? false); } /** @@ -117,26 +107,18 @@ export class Rosetta { */ public async addAssembly(assembly: spec.Assembly, assemblyDir: string) { if (await fs.pathExists(path.join(assemblyDir, DEFAULT_TABLET_NAME))) { - await this.loadTabletFromFile( - path.join(assemblyDir, DEFAULT_TABLET_NAME), - ); + await this.loadTabletFromFile(path.join(assemblyDir, DEFAULT_TABLET_NAME)); return; } if (this.options.liveConversion) { - for (const tsnip of allTypeScriptSnippets( - [{ assembly, directory: assemblyDir }], - this.loose, - )) { + for (const tsnip of allTypeScriptSnippets([{ assembly, directory: assemblyDir }], this.loose)) { this.extractedSnippets.set(tsnip.visibleSource, tsnip); } } } - public translateSnippet( - source: TypeScriptSnippet, - targetLang: TargetLanguage, - ): Translation | undefined { + public translateSnippet(source: TypeScriptSnippet, targetLang: TargetLanguage): Translation | undefined { // Look for it in loaded tablets for (const tab of this.allTablets) { const ret = tab.lookup(source, targetLang); @@ -148,10 +130,7 @@ export class Rosetta { if (!this.options.liveConversion) { return undefined; } - if ( - this.options.targetLanguages && - !this.options.targetLanguages.includes(targetLang) - ) { + if (this.options.targetLanguages && !this.options.targetLanguages.includes(targetLang)) { throw new Error( `Rosetta configured for live conversion to ${this.options.targetLanguages.join( ', ', @@ -162,19 +141,13 @@ export class Rosetta { // See if we're going to live-convert it with full source information const extracted = this.extractedSnippets.get(source.visibleSource); if (extracted !== undefined) { - const snippet = this.translator.translate( - extracted, - this.options.targetLanguages, - ); + const snippet = this.translator.translate(extracted, this.options.targetLanguages); this.liveTablet.addSnippet(snippet); return snippet.get(targetLang); } // Try to live-convert it on the spot (we won't have "where" information or fixtures) - const snippet = this.translator.translate( - source, - this.options.targetLanguages, - ); + const snippet = this.translator.translate(source, this.options.targetLanguages); return snippet.get(targetLang); } diff --git a/packages/jsii-rosetta/lib/snippet.ts b/packages/jsii-rosetta/lib/snippet.ts index ad23682d72..d585270fa1 100644 --- a/packages/jsii-rosetta/lib/snippet.ts +++ b/packages/jsii-rosetta/lib/snippet.ts @@ -41,8 +41,7 @@ export function typeScriptSnippetFromSource( strict: boolean, parameters: Record = {}, ): TypeScriptSnippet { - const [source, sourceParameters] = - parametersFromSourceDirectives(typeScriptSource); + const [source, sourceParameters] = parametersFromSourceDirectives(typeScriptSource); return { visibleSource: source.trimRight(), where, @@ -51,17 +50,10 @@ export function typeScriptSnippetFromSource( }; } -export function updateParameters( - snippet: TypeScriptSnippet, - params: Record, -): TypeScriptSnippet { +export function updateParameters(snippet: TypeScriptSnippet, params: Record): TypeScriptSnippet { return { ...snippet, - parameters: Object.assign( - Object.create(null), - snippet.parameters ?? {}, - params, - ), + parameters: Object.assign(Object.create(null), snippet.parameters ?? {}, params), }; } @@ -75,9 +67,7 @@ export function completeSource(snippet: TypeScriptSnippet) { /** * Extract snippet parameters from the first line of the source if it's a compiler directive */ -function parametersFromSourceDirectives( - source: string, -): [string, Record] { +function parametersFromSourceDirectives(source: string): [string, Record] { const [firstLine, ...rest] = source.split('\n'); // Also extract parameters from an initial line starting with '/// ' (getting rid of that line). const m = /[/]{3}(.*)$/.exec(firstLine); @@ -96,9 +86,7 @@ function parametersFromSourceDirectives( /** * Parse a set of 'param param=value' directives into an object */ -export function parseKeyValueList( - parameters: string[], -): Record { +export function parseKeyValueList(parameters: string[]): Record { const ret: Record = {}; for (const param of parameters) { const parts = param.split('=', 2); diff --git a/packages/jsii-rosetta/lib/tablets/tablets.ts b/packages/jsii-rosetta/lib/tablets/tablets.ts index 6321b4a189..a63e7d4b83 100644 --- a/packages/jsii-rosetta/lib/tablets/tablets.ts +++ b/packages/jsii-rosetta/lib/tablets/tablets.ts @@ -4,12 +4,7 @@ import * as path from 'path'; import { TargetLanguage } from '../languages'; import { TypeScriptSnippet } from '../snippet'; import { snippetKey } from './key'; -import { - TabletSchema, - TranslatedSnippetSchema, - TranslationSchema, - ORIGINAL_SNIPPET_KEY, -} from './schema'; +import { TabletSchema, TranslatedSnippetSchema, TranslationSchema, ORIGINAL_SNIPPET_KEY } from './schema'; // eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires const TOOL_VERSION = require('../../package.json').version; @@ -24,9 +19,7 @@ export class LanguageTablet { public addSnippet(snippet: TranslatedSnippet) { const existingSnippet = this.snippets[snippet.key]; - this.snippets[snippet.key] = existingSnippet - ? existingSnippet.merge(snippet) - : snippet; + this.snippets[snippet.key] = existingSnippet ? existingSnippet.merge(snippet) : snippet; } public get snippetKeys() { @@ -37,10 +30,7 @@ export class LanguageTablet { return this.snippets[key]; } - public lookup( - typeScriptSource: TypeScriptSnippet, - language: TargetLanguage, - ): Translation | undefined { + public lookup(typeScriptSource: TypeScriptSnippet, language: TargetLanguage): Translation | undefined { const snippet = this.snippets[snippetKey(typeScriptSource)]; return snippet?.get(language); } @@ -59,9 +49,7 @@ export class LanguageTablet { Object.assign( this.snippets, - mapValues(obj.snippets, (schema: TranslatedSnippetSchema) => - TranslatedSnippet.fromSchema(schema), - ), + mapValues(obj.snippets, (schema: TranslatedSnippetSchema) => TranslatedSnippet.fromSchema(schema)), ); } @@ -142,10 +130,7 @@ export class TranslatedSnippet { }; } - public addTranslatedSource( - language: TargetLanguage, - translation: string, - ): Translation { + public addTranslatedSource(language: TargetLanguage, translation: string): Translation { this.translations[language] = { source: translation }; return { @@ -156,9 +141,7 @@ export class TranslatedSnippet { } public get languages(): TargetLanguage[] { - return Object.keys(this.translations).filter( - (x) => x !== ORIGINAL_SNIPPET_KEY, - ) as TargetLanguage[]; + return Object.keys(this.translations).filter((x) => x !== ORIGINAL_SNIPPET_KEY) as TargetLanguage[]; } public get(language: TargetLanguage): Translation | undefined { @@ -196,10 +179,7 @@ export interface Translation { didCompile?: boolean; } -function mapValues( - xs: Record, - fn: (x: A) => B, -): Record { +function mapValues(xs: Record, fn: (x: A) => B): Record { const ret: Record = {}; for (const [key, value] of Object.entries(xs)) { ret[key] = fn(value); diff --git a/packages/jsii-rosetta/lib/translate.ts b/packages/jsii-rosetta/lib/translate.ts index 65d5cc643c..07c893a155 100644 --- a/packages/jsii-rosetta/lib/translate.ts +++ b/packages/jsii-rosetta/lib/translate.ts @@ -5,18 +5,11 @@ import { TARGET_LANGUAGES, TargetLanguage } from './languages'; import * as logging from './logging'; import { renderTree, Span, spanContains } from './o-tree'; import { AstRenderer, AstHandler, AstRendererOptions } from './renderer'; -import { - TypeScriptSnippet, - completeSource, - SnippetParameters, -} from './snippet'; +import { TypeScriptSnippet, completeSource, SnippetParameters } from './snippet'; import { snippetKey } from './tablets/key'; import { TranslatedSnippet } from './tablets/tablets'; import { calculateVisibleSpans } from './typescript/ast-utils'; -import { - TypeScriptCompiler, - CompilationResult, -} from './typescript/ts-compiler'; +import { TypeScriptCompiler, CompilationResult } from './typescript/ts-compiler'; import { annotateStrictDiagnostic, File } from './util'; export function translateTypeScript( @@ -24,10 +17,7 @@ export function translateTypeScript( visitor: AstHandler, options: SnippetTranslatorOptions = {}, ): TranslateResult { - const translator = new SnippetTranslator( - { visibleSource: source.contents, where: source.fileName }, - options, - ); + const translator = new SnippetTranslator({ visibleSource: source.contents, where: source.fileName }, options); const translated = translator.renderUsing(visitor); return { @@ -49,19 +39,12 @@ export class Translator { public constructor(private readonly includeCompilerDiagnostics: boolean) {} - public translate( - snip: TypeScriptSnippet, - languages: readonly TargetLanguage[] = Object.values(TargetLanguage), - ) { - logging.debug( - `Translating ${snippetKey(snip)} ${inspect(snip.parameters ?? {})}`, - ); + public translate(snip: TypeScriptSnippet, languages: readonly TargetLanguage[] = Object.values(TargetLanguage)) { + logging.debug(`Translating ${snippetKey(snip)} ${inspect(snip.parameters ?? {})}`); const translator = this.translatorFor(snip); const snippet = TranslatedSnippet.fromSnippet( snip, - this.includeCompilerDiagnostics - ? translator.compileDiagnostics.length === 0 - : undefined, + this.includeCompilerDiagnostics ? translator.compileDiagnostics.length === 0 : undefined, ); for (const lang of languages) { @@ -70,9 +53,7 @@ export class Translator { snippet.addTranslatedSource(lang, translated); } - this.#diagnostics = ts.sortAndDeduplicateDiagnostics( - this.#diagnostics.concat(translator.diagnostics), - ); + this.#diagnostics = ts.sortAndDeduplicateDiagnostics(this.#diagnostics.concat(translator.diagnostics)); return snippet; } @@ -126,21 +107,14 @@ export class SnippetTranslator { private readonly visibleSpans: Span[]; private readonly compilation!: CompilationResult; - public constructor( - snippet: TypeScriptSnippet, - private readonly options: SnippetTranslatorOptions = {}, - ) { + public constructor(snippet: TypeScriptSnippet, private readonly options: SnippetTranslatorOptions = {}) { const compiler = options.compiler ?? new TypeScriptCompiler(); const source = completeSource(snippet); const fakeCurrentDirectory = snippet.parameters?.[SnippetParameters.$COMPILATION_DIRECTORY] ?? snippet.parameters?.[SnippetParameters.$PROJECT_DIRECTORY]; - this.compilation = compiler.compileInMemory( - snippet.where, - source, - fakeCurrentDirectory, - ); + this.compilation = compiler.compileInMemory(snippet.where, source, fakeCurrentDirectory); // Respect '/// !hide' and '/// !show' directives this.visibleSpans = calculateVisibleSpans(source); @@ -150,15 +124,9 @@ export class SnippetTranslator { const program = this.compilation.program; const diagnostics = [ ...neverThrowing(program.getGlobalDiagnostics)(), - ...neverThrowing(program.getSyntacticDiagnostics)( - this.compilation.rootFile, - ), - ...neverThrowing(program.getDeclarationDiagnostics)( - this.compilation.rootFile, - ), - ...neverThrowing(program.getSemanticDiagnostics)( - this.compilation.rootFile, - ), + ...neverThrowing(program.getSyntacticDiagnostics)(this.compilation.rootFile), + ...neverThrowing(program.getDeclarationDiagnostics)(this.compilation.rootFile), + ...neverThrowing(program.getSemanticDiagnostics)(this.compilation.rootFile), ]; if (snippet.strict) { // In a strict assembly, so we'll need to brand all diagnostics here... @@ -173,9 +141,7 @@ export class SnippetTranslator { * is here to avoid compiler crashes due to broken code examples that cause * the TypeScript compiler to hit a "Debug Failure". */ - function neverThrowing( - call: (...args: A) => readonly R[], - ): (...args: A) => readonly R[] { + function neverThrowing(call: (...args: A) => readonly R[]): (...args: A) => readonly R[] { return (...args: A) => { try { return call(...args); @@ -195,30 +161,20 @@ export class SnippetTranslator { this.options, ); const converted = converter.convert(this.compilation.rootFile); - this.translateDiagnostics.push( - ...filterVisibleDiagnostics(converter.diagnostics, this.visibleSpans), - ); + this.translateDiagnostics.push(...filterVisibleDiagnostics(converter.diagnostics, this.visibleSpans)); return renderTree(converted, { visibleSpans: this.visibleSpans }); } public get diagnostics(): readonly ts.Diagnostic[] { - return ts.sortAndDeduplicateDiagnostics( - this.compileDiagnostics.concat(this.translateDiagnostics), - ); + return ts.sortAndDeduplicateDiagnostics(this.compileDiagnostics.concat(this.translateDiagnostics)); } } /** * Hide diagnostics that are rosetta-sourced if they are reported against a non-visible span */ -function filterVisibleDiagnostics( - diags: readonly ts.Diagnostic[], - visibleSpans: Span[], -): ts.Diagnostic[] { +function filterVisibleDiagnostics(diags: readonly ts.Diagnostic[], visibleSpans: Span[]): ts.Diagnostic[] { return diags.filter( - (d) => - d.source !== 'rosetta' || - d.start === undefined || - visibleSpans.some((s) => spanContains(s, d.start!)), + (d) => d.source !== 'rosetta' || d.start === undefined || visibleSpans.some((s) => spanContains(s, d.start!)), ); } diff --git a/packages/jsii-rosetta/lib/typescript/ast-utils.ts b/packages/jsii-rosetta/lib/typescript/ast-utils.ts index f1ef0ef4f6..ce0312b152 100644 --- a/packages/jsii-rosetta/lib/typescript/ast-utils.ts +++ b/packages/jsii-rosetta/lib/typescript/ast-utils.ts @@ -106,38 +106,24 @@ export function nodeChildren(node: ts.Node): ts.Node[] { * * Looks like SyntaxList nodes appear in the printed AST, but they don't actually appear */ -export function nodeOfType( - syntaxKind: ts.SyntaxKind, - children?: AstMatcher, -): AstMatcher; +export function nodeOfType(syntaxKind: ts.SyntaxKind, children?: AstMatcher): AstMatcher; // eslint-disable-next-line max-len -export function nodeOfType< - S extends keyof CapturableNodes, - N extends string, - A, ->( +export function nodeOfType( capture: N, capturableNodeType: S, children?: AstMatcher, ): AstMatcher & { [key in N]: CapturableNodes[S] }>; // eslint-disable-next-line max-len -export function nodeOfType< - S extends keyof CapturableNodes, - N extends string, - A, ->( +export function nodeOfType( syntaxKindOrCaptureName: ts.SyntaxKind | N, nodeTypeOrChildren?: S | AstMatcher, children?: AstMatcher, ): AstMatcher | AstMatcher { const capturing = typeof syntaxKindOrCaptureName === 'string'; // Determine which overload we're in (SyntaxKind is a number) - const realNext = - (capturing ? children : (nodeTypeOrChildren as AstMatcher)) ?? DONE; + const realNext = (capturing ? children : (nodeTypeOrChildren as AstMatcher)) ?? DONE; const realCapture = capturing ? (syntaxKindOrCaptureName as N) : undefined; - const realSyntaxKind = capturing - ? nodeTypeOrChildren - : syntaxKindOrCaptureName; + const realSyntaxKind = capturing ? nodeTypeOrChildren : syntaxKindOrCaptureName; return (nodes) => { for (const node of nodes ?? []) { @@ -161,9 +147,7 @@ export function nodeOfType< export function anyNode(): AstMatcher>; export function anyNode(children: AstMatcher): AstMatcher; -export function anyNode( - children?: AstMatcher, -): AstMatcher | AstMatcher { +export function anyNode(children?: AstMatcher): AstMatcher | AstMatcher { const realNext = children ?? DONE; return (nodes) => { for (const node of nodes ?? []) { @@ -207,15 +191,8 @@ export const DONE: AstMatcher> = () => ({}); /** * Run a matcher against a node and return (or invoke a callback with) the accumulated bindings */ -export function matchAst( - node: ts.Node, - matcher: AstMatcher, -): A | undefined; -export function matchAst( - node: ts.Node, - matcher: AstMatcher, - cb: (bindings: A) => void, -): boolean; +export function matchAst(node: ts.Node, matcher: AstMatcher): A | undefined; +export function matchAst(node: ts.Node, matcher: AstMatcher, cb: (bindings: A) => void): boolean; export function matchAst( node: ts.Node, matcher: AstMatcher, @@ -267,10 +244,7 @@ const WHITESPACE = [' ', '\t', '\r', '\n']; * * Rewritten because I can't get ts.getLeadingComments and ts.getTrailingComments to do what I want. */ -export function extractComments( - text: string, - start: number, -): ts.CommentRange[] { +export function extractComments(text: string, start: number): ts.CommentRange[] { return scanText(text, start) .filter((s) => s.type === 'blockcomment' || s.type === 'linecomment') .map(commentRangeFromTextRange); @@ -278,10 +252,7 @@ export function extractComments( export function commentRangeFromTextRange(rng: TextRange): ts.CommentRange { return { - kind: - rng.type === 'blockcomment' - ? ts.SyntaxKind.MultiLineCommentTrivia - : ts.SyntaxKind.SingleLineCommentTrivia, + kind: rng.type === 'blockcomment' ? ts.SyntaxKind.MultiLineCommentTrivia : ts.SyntaxKind.SingleLineCommentTrivia, pos: rng.pos, end: rng.end, hasTrailingNewLine: rng.type !== 'blockcomment' && rng.hasTrailingNewLine, @@ -301,11 +272,7 @@ interface TextRange { * Stop at 'end' when given, or the first non-whitespace character in a * non-comment if not given. */ -export function scanText( - text: string, - start: number, - end?: number, -): TextRange[] { +export function scanText(text: string, start: number, end?: number): TextRange[] { const ret: TextRange[] = []; let pos = start; @@ -405,33 +372,23 @@ export function scanText( const VOID_SHOW_KEYWORD = 'show'; -export function extractMaskingVoidExpression( - node: ts.Node, -): ts.VoidExpression | undefined { +export function extractMaskingVoidExpression(node: ts.Node): ts.VoidExpression | undefined { const expr = extractVoidExpression(node); if (!expr) { return undefined; } - if ( - ts.isStringLiteral(expr.expression) && - expr.expression.text === VOID_SHOW_KEYWORD - ) { + if (ts.isStringLiteral(expr.expression) && expr.expression.text === VOID_SHOW_KEYWORD) { return undefined; } return expr; } -export function extractShowingVoidExpression( - node: ts.Node, -): ts.VoidExpression | undefined { +export function extractShowingVoidExpression(node: ts.Node): ts.VoidExpression | undefined { const expr = extractVoidExpression(node); if (!expr) { return undefined; } - if ( - ts.isStringLiteral(expr.expression) && - expr.expression.text === VOID_SHOW_KEYWORD - ) { + if (ts.isStringLiteral(expr.expression) && expr.expression.text === VOID_SHOW_KEYWORD) { return expr; } return undefined; @@ -440,9 +397,7 @@ export function extractShowingVoidExpression( /** * Return the string argument to a void expression if it exists */ -export function voidExpressionString( - node: ts.VoidExpression, -): string | undefined { +export function voidExpressionString(node: ts.VoidExpression): string | undefined { if (ts.isStringLiteral(node.expression)) { return node.expression.text; } @@ -452,9 +407,7 @@ export function voidExpressionString( /** * We use void directives as pragmas. Extract the void directives here */ -export function extractVoidExpression( - node: ts.Node, -): ts.VoidExpression | undefined { +export function extractVoidExpression(node: ts.Node): ts.VoidExpression | undefined { if (ts.isVoidExpression(node)) { return node; } @@ -464,10 +417,7 @@ export function extractVoidExpression( if (ts.isParenthesizedExpression(node)) { return extractVoidExpression(node.expression); } - if ( - ts.isBinaryExpression(node) && - node.operatorToken.kind === ts.SyntaxKind.CommaToken - ) { + if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) { return extractVoidExpression(node.left); } return undefined; @@ -477,9 +427,7 @@ export function quoteStringLiteral(x: string) { return x.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } -export function visibility( - x: ts.PropertyLikeDeclaration | ts.FunctionLikeDeclarationBase, -) { +export function visibility(x: ts.PropertyLikeDeclaration | ts.FunctionLikeDeclarationBase) { const flags = ts.getCombinedModifierFlags(x); if (flags & ts.ModifierFlags.Private) { return 'private'; @@ -490,9 +438,7 @@ export function visibility( return 'public'; } -export function isReadOnly( - x: ts.PropertyLikeDeclaration | ts.FunctionLikeDeclarationBase, -) { +export function isReadOnly(x: ts.PropertyLikeDeclaration | ts.FunctionLikeDeclarationBase) { const flags = ts.getCombinedModifierFlags(x); return (flags & ts.ModifierFlags.Readonly) !== 0; } @@ -531,14 +477,7 @@ export function findSuperCall( /** * Return the names of all private property declarations */ -export function privatePropertyNames( - members: readonly ts.ClassElement[], - renderer: AstRenderer, -): string[] { - const props = members.filter((m) => - ts.isPropertyDeclaration(m), - ) as ts.PropertyDeclaration[]; - return props - .filter((m) => visibility(m) === 'private') - .map((m) => renderer.textOf(m.name)); +export function privatePropertyNames(members: readonly ts.ClassElement[], renderer: AstRenderer): string[] { + const props = members.filter((m) => ts.isPropertyDeclaration(m)) as ts.PropertyDeclaration[]; + return props.filter((m) => visibility(m) === 'private').map((m) => renderer.textOf(m.name)); } diff --git a/packages/jsii-rosetta/lib/typescript/imports.ts b/packages/jsii-rosetta/lib/typescript/imports.ts index 0c872562e4..ae7fbe33f0 100644 --- a/packages/jsii-rosetta/lib/typescript/imports.ts +++ b/packages/jsii-rosetta/lib/typescript/imports.ts @@ -1,12 +1,7 @@ import * as ts from 'typescript'; import { AstRenderer } from '../renderer'; -import { - allOfType, - matchAst, - nodeOfType, - stringFromLiteral, -} from './ast-utils'; +import { allOfType, matchAst, nodeOfType, stringFromLiteral } from './ast-utils'; /** * Our own unification of import statements @@ -28,18 +23,11 @@ export interface ImportBinding { alias?: string; } -export function analyzeImportEquals( - node: ts.ImportEqualsDeclaration, - context: AstRenderer, -): ImportStatement { +export function analyzeImportEquals(node: ts.ImportEqualsDeclaration, context: AstRenderer): ImportStatement { let moduleName = '???'; - matchAst( - node.moduleReference, - nodeOfType('ref', ts.SyntaxKind.ExternalModuleReference), - (bindings) => { - moduleName = stringFromLiteral(bindings.ref.expression); - }, - ); + matchAst(node.moduleReference, nodeOfType('ref', ts.SyntaxKind.ExternalModuleReference), (bindings) => { + moduleName = stringFromLiteral(bindings.ref.expression); + }); return { node, @@ -48,20 +36,14 @@ export function analyzeImportEquals( }; } -export function analyzeImportDeclaration( - node: ts.ImportDeclaration, - context: AstRenderer, -): ImportStatement { +export function analyzeImportDeclaration(node: ts.ImportDeclaration, context: AstRenderer): ImportStatement { const packageName = stringFromLiteral(node.moduleSpecifier); const starBindings = matchAst( node, nodeOfType( ts.SyntaxKind.ImportDeclaration, - nodeOfType( - ts.SyntaxKind.ImportClause, - nodeOfType('namespace', ts.SyntaxKind.NamespaceImport), - ), + nodeOfType(ts.SyntaxKind.ImportClause, nodeOfType('namespace', ts.SyntaxKind.NamespaceImport)), ), ); @@ -82,10 +64,7 @@ export function analyzeImportDeclaration( ts.SyntaxKind.ImportDeclaration, nodeOfType( ts.SyntaxKind.ImportClause, - nodeOfType( - ts.SyntaxKind.NamedImports, - allOfType(ts.SyntaxKind.ImportSpecifier, 'specifiers'), - ), + nodeOfType(ts.SyntaxKind.NamedImports, allOfType(ts.SyntaxKind.ImportSpecifier, 'specifiers')), ), ), ); diff --git a/packages/jsii-rosetta/lib/typescript/ts-compiler.ts b/packages/jsii-rosetta/lib/typescript/ts-compiler.ts index c73b97d55d..4d58a27d68 100644 --- a/packages/jsii-rosetta/lib/typescript/ts-compiler.ts +++ b/packages/jsii-rosetta/lib/typescript/ts-compiler.ts @@ -1,10 +1,7 @@ import * as ts from 'typescript'; export class TypeScriptCompiler { - private readonly realHost = ts.createCompilerHost( - STANDARD_COMPILER_OPTIONS, - true, - ); + private readonly realHost = ts.createCompilerHost(STANDARD_COMPILER_OPTIONS, true); public createInMemoryCompilerHost( sourcePath: string, @@ -12,45 +9,22 @@ export class TypeScriptCompiler { currentDirectory?: string, ): ts.CompilerHost { const realHost = this.realHost; - const sourceFile = ts.createSourceFile( - sourcePath, - sourceContents, - ts.ScriptTarget.Latest, - ); + const sourceFile = ts.createSourceFile(sourcePath, sourceContents, ts.ScriptTarget.Latest); return { ...realHost, - fileExists: (filePath) => - filePath === sourcePath || realHost.fileExists(filePath), - getCurrentDirectory: - currentDirectory != null - ? () => currentDirectory - : realHost.getCurrentDirectory, - getSourceFile: ( - fileName, - languageVersion, - onError, - shouldCreateNewSourceFile, - ) => + fileExists: (filePath) => filePath === sourcePath || realHost.fileExists(filePath), + getCurrentDirectory: currentDirectory != null ? () => currentDirectory : realHost.getCurrentDirectory, + getSourceFile: (fileName, languageVersion, onError, shouldCreateNewSourceFile) => fileName === sourcePath ? sourceFile - : realHost.getSourceFile( - fileName, - languageVersion, - onError, - shouldCreateNewSourceFile, - ), - readFile: (filePath) => - filePath === sourcePath ? sourceContents : realHost.readFile(filePath), + : realHost.getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile), + readFile: (filePath) => (filePath === sourcePath ? sourceContents : realHost.readFile(filePath)), writeFile: () => void undefined, }; } - public compileInMemory( - filename: string, - contents: string, - currentDirectory?: string, - ): CompilationResult { + public compileInMemory(filename: string, contents: string, currentDirectory?: string): CompilationResult { if (!filename.endsWith('.ts')) { // Necessary or the TypeScript compiler won't compile the file. filename += '.ts'; @@ -59,11 +33,7 @@ export class TypeScriptCompiler { const program = ts.createProgram({ rootNames: [filename], options: STANDARD_COMPILER_OPTIONS, - host: this.createInMemoryCompilerHost( - filename, - contents, - currentDirectory, - ), + host: this.createInMemoryCompilerHost(filename, contents, currentDirectory), }); const rootFile = program.getSourceFile(filename); diff --git a/packages/jsii-rosetta/lib/typescript/types.ts b/packages/jsii-rosetta/lib/typescript/types.ts index 32ed14347a..1bc1d76188 100644 --- a/packages/jsii-rosetta/lib/typescript/types.ts +++ b/packages/jsii-rosetta/lib/typescript/types.ts @@ -5,15 +5,11 @@ import { AstRenderer } from '../renderer'; /** * Return the OTHER type from undefined from a union, returns undefined if there is more than one */ -export function typeWithoutUndefinedUnion( - type: ts.Type | undefined, -): ts.Type | undefined { +export function typeWithoutUndefinedUnion(type: ts.Type | undefined): ts.Type | undefined { if (!type || !type.isUnion()) { return type; } - const remaining = type.types.filter( - (t) => t.flags !== ts.TypeFlags.Undefined, - ); + const remaining = type.types.filter((t) => t.flags !== ts.TypeFlags.Undefined); if (remaining.length > 1) { return undefined; } @@ -35,10 +31,7 @@ export function builtInTypeName(type: ts.Type): BuiltInType | undefined { return map[type.flags]; } -export function parameterAcceptsUndefined( - param: ts.ParameterDeclaration, - type?: ts.Type, -): boolean { +export function parameterAcceptsUndefined(param: ts.ParameterDeclaration, type?: ts.Type): boolean { if (param.initializer !== undefined) { return true; } @@ -67,10 +60,7 @@ export function typeContainsUndefined(type: ts.Type): boolean { /** * If this is a map type, return the type mapped *to* (key must always be `string` anyway). */ -export function mapElementType( - type: ts.Type, - renderer: AstRenderer, -): ts.Type | undefined { +export function mapElementType(type: ts.Type, renderer: AstRenderer): ts.Type | undefined { if (type.flags & ts.TypeFlags.Object && type.symbol) { if (type.symbol.name === '__type') { // Declared map type: {[k: string]: A} @@ -101,11 +91,7 @@ export function inferMapElementType( renderer: AstRenderer, ): ts.Type | undefined { return typeIfSame( - elements.map((el) => - ts.isPropertyAssignment(el) - ? renderer.typeOfExpression(el.initializer) - : undefined, - ), + elements.map((el) => (ts.isPropertyAssignment(el) ? renderer.typeOfExpression(el.initializer) : undefined)), ); } diff --git a/packages/jsii-rosetta/lib/util.ts b/packages/jsii-rosetta/lib/util.ts index 888cc672bb..b08cd6f81b 100644 --- a/packages/jsii-rosetta/lib/util.ts +++ b/packages/jsii-rosetta/lib/util.ts @@ -9,19 +9,13 @@ export interface File { readonly fileName: string; } -export function printDiagnostics( - diags: readonly ts.Diagnostic[], - stream: NodeJS.WritableStream, -) { +export function printDiagnostics(diags: readonly ts.Diagnostic[], stream: NodeJS.WritableStream) { for (const diag of diags) { printDiagnostic(diag, stream); } } -export function printDiagnostic( - diag: ts.Diagnostic, - stream: NodeJS.WritableStream, -) { +export function printDiagnostic(diag: ts.Diagnostic, stream: NodeJS.WritableStream) { const host = { getCurrentDirectory() { return '.'; @@ -52,13 +46,9 @@ export function annotateStrictDiagnostic(diag: ts.Diagnostic) { }); } -export function isErrorDiagnostic( - diag: ts.Diagnostic, - { onlyStrict }: { readonly onlyStrict: boolean }, -): boolean { +export function isErrorDiagnostic(diag: ts.Diagnostic, { onlyStrict }: { readonly onlyStrict: boolean }): boolean { return ( - diag.category === ts.DiagnosticCategory.Error && - (!onlyStrict || !!(diag as MaybeStrictDiagnostic)[StrictBrand]) + diag.category === ts.DiagnosticCategory.Error && (!onlyStrict || !!(diag as MaybeStrictDiagnostic)[StrictBrand]) ); } diff --git a/packages/jsii-rosetta/test/commands/transliterate.test.ts b/packages/jsii-rosetta/test/commands/transliterate.test.ts index c436098e06..4c125c10af 100644 --- a/packages/jsii-rosetta/test/commands/transliterate.test.ts +++ b/packages/jsii-rosetta/test/commands/transliterate.test.ts @@ -88,13 +88,9 @@ export class ClassName implements IInterface { } }`, }); - fs.writeJsonSync( - path.join(tmpDir, SPEC_FILE_NAME), - compilationResult.assembly, - { - spaces: 2, - }, - ); + fs.writeJsonSync(path.join(tmpDir, SPEC_FILE_NAME), compilationResult.assembly, { + spaces: 2, + }); for (const [file, content] of Object.entries(compilationResult.files)) { fs.writeFileSync(path.resolve(tmpDir, file), content, 'utf-8'); } @@ -113,9 +109,7 @@ export class ClassName implements IInterface { ).resolves.not.toThrow(); // THEN - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.csharp`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.csharp`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -393,9 +387,7 @@ export class ClassName implements IInterface { } `, ); - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.java`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.java`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -673,9 +665,7 @@ export class ClassName implements IInterface { } `, ); - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.python`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.python`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -1001,13 +991,9 @@ import { SampleClass } from './index'; new SampleClass('omitted-literate'); `, }); - fs.writeJsonSync( - path.join(tmpDir, SPEC_FILE_NAME), - compilationResult.assembly, - { - spaces: 2, - }, - ); + fs.writeJsonSync(path.join(tmpDir, SPEC_FILE_NAME), compilationResult.assembly, { + spaces: 2, + }); for (const [file, content] of Object.entries(compilationResult.files)) { if (file.startsWith('omit-')) { continue; @@ -1023,9 +1009,7 @@ new SampleClass('omitted-literate'); ).resolves.not.toThrow(); // THEN - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.csharp`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.csharp`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -1130,9 +1114,7 @@ new SampleClass('omitted-literate'); `, ); - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.java`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.java`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -1237,9 +1219,7 @@ new SampleClass('omitted-literate'); `, ); - expect( - fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.python`)), - ).toMatchInlineSnapshot( + expect(fs.readJsonSync(path.join(tmpDir, `${SPEC_FILE_NAME}.python`))).toMatchInlineSnapshot( { fingerprint: expect.any(String), jsiiVersion: expect.any(String), @@ -1345,11 +1325,7 @@ new SampleClass('omitted-literate'); ); })); -async function withTemporaryDirectory( - callback: (dir: string) => Promise, -): Promise { - const tmpdir = fs.mkdtempSync( - path.join(os.tmpdir(), path.basename(__filename)), - ); +async function withTemporaryDirectory(callback: (dir: string) => Promise): Promise { + const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), path.basename(__filename))); return callback(tmpdir).finally(() => fs.removeSync(tmpdir)); } diff --git a/packages/jsii-rosetta/test/fixtures.test.ts b/packages/jsii-rosetta/test/fixtures.test.ts index d315e86b17..f11b6927bf 100644 --- a/packages/jsii-rosetta/test/fixtures.test.ts +++ b/packages/jsii-rosetta/test/fixtures.test.ts @@ -32,8 +32,7 @@ const val = new Cls();`; const fixturizedSnippet = fixturize(snippet); - expect(fixturizedSnippet.completeSource) - .toBe(`// Hoisted imports begin after !show marker below + expect(fixturizedSnippet.completeSource).toBe(`// Hoisted imports begin after !show marker below /// !show import * as ns from 'mod'; declare const mock: Tpe; diff --git a/packages/jsii-rosetta/test/jsii/assemblies.test.ts b/packages/jsii-rosetta/test/jsii/assemblies.test.ts index 0e5d67d1e3..bbcf8fe5d7 100644 --- a/packages/jsii-rosetta/test/jsii/assemblies.test.ts +++ b/packages/jsii-rosetta/test/jsii/assemblies.test.ts @@ -12,13 +12,7 @@ test('Extract snippet from README', () => { { assembly: fakeAssembly({ readme: { - markdown: [ - 'Before the example.', - '```ts', - 'someExample();', - '```', - 'After the example.', - ].join('\n'), + markdown: ['Before the example.', '```ts', 'someExample();', '```', 'After the example.'].join('\n'), }, }), directory: path.join(__dirname, 'fixtures'), @@ -37,13 +31,7 @@ test('Extract snippet from submodule READMEs', () => { submodules: { 'my.submodule': { readme: { - markdown: [ - 'Before the example.', - '```ts', - 'someExample();', - '```', - 'After the example.', - ].join('\n'), + markdown: ['Before the example.', '```ts', 'someExample();', '```', 'After the example.'].join('\n'), }, }, }, @@ -69,13 +57,7 @@ test('Extract snippet from type docstring', () => { name: 'MyType', docs: { summary: 'My Type', - remarks: [ - 'Before the example.', - '```ts', - 'someExample();', - '```', - 'After the example.', - ].join('\n'), + remarks: ['Before the example.', '```ts', 'someExample();', '```', 'After the example.'].join('\n'), }, }, }, @@ -176,12 +158,9 @@ test('Fixture allows use of import statements', () => { fqn: 'asm.MyType', name: 'MyType', docs: { - example: [ - '/// fixture=explicit', - 'import { exit } from "process";', - 'someExample();', - 'exit(0);', - ].join('\n'), + example: ['/// fixture=explicit', 'import { exit } from "process";', 'someExample();', 'exit(0);'].join( + '\n', + ), }, }, }, @@ -213,9 +192,7 @@ test('Fixture allows use of import statements', () => { " `); expect(snippets[0].visibleSource).toEqual( - ['import { exit } from "process";', 'someExample();', 'exit(0);'].join( - '\n', - ), + ['import { exit } from "process";', 'someExample();', 'exit(0);'].join('\n'), ); }); @@ -246,9 +223,9 @@ test('Backwards compatibility with literate integ tests', () => { expect(snippets[0].visibleSource).toEqual('someExample();'); expect(snippets[0].completeSource).toEqual('# Some literate source file'); - expect( - snippets[0]?.parameters?.[SnippetParameters.$COMPILATION_DIRECTORY], - ).toEqual(path.normalize('/package/test')); + expect(snippets[0]?.parameters?.[SnippetParameters.$COMPILATION_DIRECTORY]).toEqual( + path.normalize('/package/test'), + ); } finally { mockfs.restore(); } diff --git a/packages/jsii-rosetta/test/jsii/astutils.test.ts b/packages/jsii-rosetta/test/jsii/astutils.test.ts index 6021997013..f5728cc291 100644 --- a/packages/jsii-rosetta/test/jsii/astutils.test.ts +++ b/packages/jsii-rosetta/test/jsii/astutils.test.ts @@ -1,19 +1,13 @@ import { calculateVisibleSpans } from '../../lib/typescript/ast-utils'; test('full text visible by default', () => { - expect(calculateVisibleSpans('asdf')).toEqual([ - { start: 0, end: 4, visible: true }, - ]); + expect(calculateVisibleSpans('asdf')).toEqual([{ start: 0, end: 4, visible: true }]); }); test('initial span visible if directive is hiding', () => { - expect(calculateVisibleSpans('asdf\n/// !hide\nxyz')).toEqual([ - { start: 0, end: 5, visible: true }, - ]); + expect(calculateVisibleSpans('asdf\n/// !hide\nxyz')).toEqual([{ start: 0, end: 5, visible: true }]); }); test('initial span invisible if directive is showing', () => { - expect(calculateVisibleSpans('asdf\n/// !show\nxyz')).toEqual([ - { start: 14, end: 18, visible: true }, - ]); + expect(calculateVisibleSpans('asdf\n/// !show\nxyz')).toEqual([{ start: 14, end: 18, visible: true }]); }); diff --git a/packages/jsii-rosetta/test/markdown/roundtrip.test.ts b/packages/jsii-rosetta/test/markdown/roundtrip.test.ts index 5af9cb4bcb..70fbfdf23f 100644 --- a/packages/jsii-rosetta/test/markdown/roundtrip.test.ts +++ b/packages/jsii-rosetta/test/markdown/roundtrip.test.ts @@ -96,9 +96,7 @@ This is a [` + test('fenced code block', () => { expectOutput( ['before', '```ts', 'banana', ' second', '```', 'after'].join('\n'), - ['before', '', '```ts', 'banana', ' second', '```', '', 'after'].join( - '\n', - ), + ['before', '', '```ts', 'banana', ' second', '```', '', 'after'].join('\n'), ); }); diff --git a/packages/jsii-rosetta/test/otree.test.ts b/packages/jsii-rosetta/test/otree.test.ts index a1fae4e4f0..8e5e091bbe 100644 --- a/packages/jsii-rosetta/test/otree.test.ts +++ b/packages/jsii-rosetta/test/otree.test.ts @@ -11,15 +11,11 @@ test('test indentation', () => { }); test('collapse subsequent unused indentation', () => { - const tree = new OTree( - ['{'], - [new OTree([], ['\na', '\nb', '\nc'], { indent: 4, separator: ', ' })], - { - separator: ', ', - indent: 4, - suffix: '\n}', - }, - ); + const tree = new OTree(['{'], [new OTree([], ['\na', '\nb', '\nc'], { indent: 4, separator: ', ' })], { + separator: ', ', + indent: 4, + suffix: '\n}', + }); expect(renderTree(tree)).toEqual('{\n a,\n b,\n c\n}'); }); @@ -44,16 +40,6 @@ test('don not collapse subsequent USED indentation', () => { ); expect(renderTree(tree)).toEqual( - [ - '{', - ' a,', - ' {', - ' a,', - ' b,', - ' c', - ' },', - ' b', - '}', - ].join('\n'), + ['{', ' a,', ' {', ' a,', ' b,', ' c', ' },', ' b', '}'].join('\n'), ); }); diff --git a/packages/jsii-rosetta/test/rosetta.test.ts b/packages/jsii-rosetta/test/rosetta.test.ts index dadcc54130..5b9eba7e89 100644 --- a/packages/jsii-rosetta/test/rosetta.test.ts +++ b/packages/jsii-rosetta/test/rosetta.test.ts @@ -1,12 +1,6 @@ import * as mockfs from 'mock-fs'; -import { - Rosetta, - LanguageTablet, - TranslatedSnippet, - TypeScriptSnippet, - DEFAULT_TABLET_NAME, -} from '../lib'; +import { Rosetta, LanguageTablet, TranslatedSnippet, TypeScriptSnippet, DEFAULT_TABLET_NAME } from '../lib'; import { TargetLanguage } from '../lib/languages'; import { fakeAssembly } from './jsii/fake-assembly'; @@ -23,10 +17,7 @@ test('Rosetta object can do live translation', () => { }); // WHEN - const translated = rosetta.translateSnippet( - SAMPLE_CODE, - TargetLanguage.PYTHON, - ); + const translated = rosetta.translateSnippet(SAMPLE_CODE, TargetLanguage.PYTHON); // THEN expect(translated).toMatchObject({ @@ -50,10 +41,7 @@ test('Can use preloaded tablet', () => { rosetta.addTablet(tablet); // WHEN - const translated = rosetta.translateSnippet( - SAMPLE_CODE, - TargetLanguage.PYTHON, - ); + const translated = rosetta.translateSnippet(SAMPLE_CODE, TargetLanguage.PYTHON); // THEN expect(translated).toMatchObject({ @@ -70,10 +58,7 @@ test('Rosetta object can do live translation', () => { }); // WHEN - const translated = rosetta.translateSnippet( - SAMPLE_CODE, - TargetLanguage.PYTHON, - ); + const translated = rosetta.translateSnippet(SAMPLE_CODE, TargetLanguage.PYTHON); // THEN expect(translated).toMatchObject({ @@ -151,10 +136,7 @@ describe('with mocked filesystem', () => { // WHEN const rosetta = new Rosetta(); await rosetta.loadTabletFromFile('/test.tablet'); - const translated = rosetta.translateSnippet( - SAMPLE_CODE, - TargetLanguage.PYTHON, - ); + const translated = rosetta.translateSnippet(SAMPLE_CODE, TargetLanguage.PYTHON); // THEN expect(translated).toMatchObject({ @@ -170,10 +152,7 @@ describe('with mocked filesystem', () => { // WHEN const rosetta = new Rosetta(); await rosetta.addAssembly(fakeAssembly({}), '/'); - const translated = rosetta.translateSnippet( - SAMPLE_CODE, - TargetLanguage.PYTHON, - ); + const translated = rosetta.translateSnippet(SAMPLE_CODE, TargetLanguage.PYTHON); // THEN expect(translated).toMatchObject({ @@ -183,10 +162,7 @@ describe('with mocked filesystem', () => { }); }); -function makeSnippet( - original: TypeScriptSnippet, - translations: Record, -) { +function makeSnippet(original: TypeScriptSnippet, translations: Record) { const snippet = TranslatedSnippet.fromSnippet(original); for (const [key, value] of Object.entries(translations)) { snippet.addTranslatedSource(key as TargetLanguage, value); diff --git a/packages/jsii-rosetta/test/translate.test.ts b/packages/jsii-rosetta/test/translate.test.ts index 3ddbe50456..fbdfa809ae 100644 --- a/packages/jsii-rosetta/test/translate.test.ts +++ b/packages/jsii-rosetta/test/translate.test.ts @@ -4,8 +4,7 @@ import { VisualizeAstVisitor } from '../lib/languages/visualize'; test('does not fail on "Debug Failure"', () => { // GIVEN const snippet: TypeScriptSnippet = { - completeSource: - 'Missing literate source file test/integ.restapi-import.lit.ts', + completeSource: 'Missing literate source file test/integ.restapi-import.lit.ts', where: '@aws-cdk.aws-apigateway-README-snippet4', visibleSource: "import { App, CfnOutput, NestedStack, NestedStackProps, Stack } from '@aws-cdk/core';\nimport { Construct } from 'constructs';\nimport { Deployment, Method, MockIntegration, PassthroughBehavior, RestApi, Stage } from '../lib';\n\n/**\n * This file showcases how to split up a RestApi's Resources and Methods across nested stacks.\n *\n * The root stack 'RootStack' first defines a RestApi.\n * Two nested stacks BooksStack and PetsStack, create corresponding Resources '/books' and '/pets'.\n * They are then…;\n\n readonly methods?: Method[];\n}\n\nclass DeployStack extends NestedStack {\n constructor(scope: Construct, props: DeployStackProps) {\n super(scope, 'integ-restapi-import-DeployStack', props);\n\n const deployment = new Deployment(this, 'Deployment', {\n api: RestApi.fromRestApiId(this, 'RestApi', props.restApiId),\n });\n (props.methods ?? []).forEach((method) => deployment.node.addDependency(method));\n new Stage(this, 'Stage', { deployment });\n }\n}\n\nnew RootStack(new App());", diff --git a/packages/jsii-rosetta/test/translations.test.ts b/packages/jsii-rosetta/test/translations.test.ts index ec5619067b..3e962e0793 100644 --- a/packages/jsii-rosetta/test/translations.test.ts +++ b/packages/jsii-rosetta/test/translations.test.ts @@ -54,10 +54,7 @@ const typeScriptTests = allFiles(translationsRoot) .filter((f) => !f.endsWith('.test.ts')); // Exclude self and other jest tests in this dir for (const typeScriptTest of typeScriptTests) { - describe(`Translating ${path.relative( - translationsRoot, - typeScriptTest, - )}`, () => { + describe(`Translating ${path.relative(translationsRoot, typeScriptTest)}`, () => { const typeScriptSource = fs.readFileSync(typeScriptTest, { encoding: 'utf-8', }); @@ -90,9 +87,7 @@ for (const typeScriptTest of typeScriptTests) { const expected = fs.readFileSync(languageFile, { encoding: 'utf-8' }); try { const translation = translator.renderUsing(visitor); - expect(stripEmptyLines(translation)).toEqual( - stripEmptyLines(stripCommonWhitespace(expected)), - ); + expect(stripEmptyLines(translation)).toEqual(stripEmptyLines(stripCommonWhitespace(expected))); } catch (e) { anyFailed = true; throw e; diff --git a/packages/jsii-rosetta/test/util.test.ts b/packages/jsii-rosetta/test/util.test.ts index c565782502..15395954c7 100644 --- a/packages/jsii-rosetta/test/util.test.ts +++ b/packages/jsii-rosetta/test/util.test.ts @@ -1,10 +1,6 @@ import * as ts from 'typescript'; -import { - StrictBrand, - annotateStrictDiagnostic, - isErrorDiagnostic, -} from '../lib/util'; +import { StrictBrand, annotateStrictDiagnostic, isErrorDiagnostic } from '../lib/util'; describe(annotateStrictDiagnostic, () => { const diagnostic = { @@ -30,26 +26,23 @@ describe(isErrorDiagnostic, () => { ...makeDiagnostic(ts.DiagnosticCategory.Error), [StrictBrand]: true, }; - const diagnostics = [ - warningDiagnostic, - errorDiagnostic, - strictErrorDiagnostic, - ]; + const diagnostics = [warningDiagnostic, errorDiagnostic, strictErrorDiagnostic]; test('returns all error diagnostics if onlyStrict is false', () => { const onlyStrict = false; - expect( - diagnostics.filter((diag) => isErrorDiagnostic(diag, { onlyStrict })), - ).toStrictEqual([errorDiagnostic, strictErrorDiagnostic]); + expect(diagnostics.filter((diag) => isErrorDiagnostic(diag, { onlyStrict }))).toStrictEqual([ + errorDiagnostic, + strictErrorDiagnostic, + ]); }); test('returns only strict error diagnostics if onlyStrict is true', () => { const onlyStrict = true; - expect( - diagnostics.filter((diag) => isErrorDiagnostic(diag, { onlyStrict })), - ).toStrictEqual([strictErrorDiagnostic]); + expect(diagnostics.filter((diag) => isErrorDiagnostic(diag, { onlyStrict }))).toStrictEqual([ + strictErrorDiagnostic, + ]); }); }); From 4630ad3442a211725d64d950942fa8d3385b9bec Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 7 Oct 2021 11:33:38 +0200 Subject: [PATCH 2/2] fix(rosetta): fix handling of structs starting with `I` Not all interfaces that start with the name `I` are structs: only if the second letter is also uppercase. Also clean up type handling a bit, we were doing some things that the TypeScript compiler has built-ins for. --- packages/jsii-rosetta/lib/commands/extract.ts | 2 +- packages/jsii-rosetta/lib/jsii/jsii-utils.ts | 3 +- packages/jsii-rosetta/lib/languages/csharp.ts | 6 +- .../jsii-rosetta/lib/languages/default.ts | 5 +- packages/jsii-rosetta/lib/languages/java.ts | 8 +- packages/jsii-rosetta/lib/renderer.ts | 10 ++- packages/jsii-rosetta/lib/typescript/types.ts | 80 ++++++++++++++----- .../jsii-rosetta/test/translations.test.ts | 6 +- .../structs/struct_starting_with_i.cs | 3 + .../structs/struct_starting_with_i.java | 2 + .../structs/struct_starting_with_i.py | 3 + .../structs/struct_starting_with_i.ts | 9 +++ 12 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.cs create mode 100644 packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.java create mode 100644 packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.py create mode 100644 packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.ts diff --git a/packages/jsii-rosetta/lib/commands/extract.ts b/packages/jsii-rosetta/lib/commands/extract.ts index fb8f4c42dd..45df3ec3ee 100644 --- a/packages/jsii-rosetta/lib/commands/extract.ts +++ b/packages/jsii-rosetta/lib/commands/extract.ts @@ -123,7 +123,7 @@ export function singleThreadedTranslateAll( failures.push({ category: ts.DiagnosticCategory.Error, code: 999, - messageText: `rosetta: error translating snippet: ${e}\n${block.completeSource}`, + messageText: `rosetta: error translating snippet: ${e}\n${e.stack}\n${block.completeSource}`, file: undefined, start: undefined, length: undefined, diff --git a/packages/jsii-rosetta/lib/jsii/jsii-utils.ts b/packages/jsii-rosetta/lib/jsii/jsii-utils.ts index 40ad0aa827..ab949d5b89 100644 --- a/packages/jsii-rosetta/lib/jsii/jsii-utils.ts +++ b/packages/jsii-rosetta/lib/jsii/jsii-utils.ts @@ -4,7 +4,8 @@ import { AstRenderer } from '../renderer'; import { typeContainsUndefined } from '../typescript/types'; export function isStructInterface(name: string) { - return !name.startsWith('I'); + // Start with an I and another uppercase character + return !/^I[A-Z]/.test(name); } export function isStructType(type: ts.Type) { diff --git a/packages/jsii-rosetta/lib/languages/csharp.ts b/packages/jsii-rosetta/lib/languages/csharp.ts index 27844823c3..5eb900cb28 100644 --- a/packages/jsii-rosetta/lib/languages/csharp.ts +++ b/packages/jsii-rosetta/lib/languages/csharp.ts @@ -14,12 +14,12 @@ import { } from '../typescript/ast-utils'; import { ImportStatement } from '../typescript/imports'; import { - typeWithoutUndefinedUnion, builtInTypeName, typeContainsUndefined, parameterAcceptsUndefined, mapElementType, inferMapElementType, + firstTypeInUnion, } from '../typescript/types'; import { flat, partition, setExtend } from '../util'; import { DefaultVisitor } from './default'; @@ -623,9 +623,9 @@ export class CSharpVisitor extends DefaultVisitor { return fallback; } - const nonUnionType = typeWithoutUndefinedUnion(type); + const nonUnionType = firstTypeInUnion(renderer.typeChecker, type); if (!nonUnionType) { - renderer.report(typeNode, 'Type unions in examples are not supported'); + renderer.report(typeNode, 'Type unions in examples are not supported for csharp'); return '...'; } diff --git a/packages/jsii-rosetta/lib/languages/default.ts b/packages/jsii-rosetta/lib/languages/default.ts index 36c79ce586..cd98312a4d 100644 --- a/packages/jsii-rosetta/lib/languages/default.ts +++ b/packages/jsii-rosetta/lib/languages/default.ts @@ -5,7 +5,7 @@ import { OTree, NO_SYNTAX } from '../o-tree'; import { AstRenderer, AstHandler, nimpl, CommentSyntax } from '../renderer'; import { voidExpressionString } from '../typescript/ast-utils'; import { ImportStatement } from '../typescript/imports'; -import { mapElementType, typeWithoutUndefinedUnion } from '../typescript/types'; +import { mapElementType } from '../typescript/types'; import { TargetLanguage } from '.'; @@ -130,7 +130,7 @@ export abstract class DefaultVisitor implements AstHandler { * - It's not a struct (render as key-value map) */ public objectLiteralExpression(node: ts.ObjectLiteralExpression, context: AstRenderer): OTree { - const type = typeWithoutUndefinedUnion(context.inferredTypeOfExpression(node)); + const type = context.inferredTypeOfExpression(node); const isUnknownType = !type || !type.symbol; const isKnownStruct = type && isStructType(type); @@ -141,6 +141,7 @@ export abstract class DefaultVisitor implements AstHandler { if (isKnownStruct) { return this.knownStructObjectLiteralExpression(node, type!, context); } + return this.keyValueObjectLiteralExpression(node, type && mapElementType(type, context), context); } diff --git a/packages/jsii-rosetta/lib/languages/java.ts b/packages/jsii-rosetta/lib/languages/java.ts index 26e7cd0dc1..3ffe97957e 100644 --- a/packages/jsii-rosetta/lib/languages/java.ts +++ b/packages/jsii-rosetta/lib/languages/java.ts @@ -7,7 +7,7 @@ import { OTree, NO_SYNTAX } from '../o-tree'; import { AstRenderer } from '../renderer'; import { isReadOnly, matchAst, nodeOfType, quoteStringLiteral, visibility } from '../typescript/ast-utils'; import { ImportStatement } from '../typescript/imports'; -import { builtInTypeName, mapElementType, typeWithoutUndefinedUnion } from '../typescript/types'; +import { builtInTypeName, firstTypeInUnion, mapElementType } from '../typescript/types'; import { DefaultVisitor } from './default'; interface JavaContext { @@ -407,7 +407,7 @@ export class JavaVisitor extends DefaultVisitor { const argsLength = node.arguments ? node.arguments.length : 0; const lastArg = argsLength > 0 ? node.arguments![argsLength - 1] : undefined; const lastArgIsObjectLiteral = lastArg && ts.isObjectLiteralExpression(lastArg); - const lastArgType = lastArg && typeWithoutUndefinedUnion(renderer.inferredTypeOfExpression(lastArg)); + const lastArgType = lastArg && renderer.inferredTypeOfExpression(lastArg); // we only render the ClassName.Builder.create(...) expression // if the last argument is an object literal, and NOT a known struct // (in that case, it has its own creation method) @@ -654,9 +654,9 @@ export class JavaVisitor extends DefaultVisitor { return fallback; } - const nonUnionType = typeWithoutUndefinedUnion(type); + const nonUnionType = firstTypeInUnion(renderer.typeChecker, type); if (!nonUnionType) { - renderer.report(owningNode, 'Type unions in examples are not supported'); + renderer.report(owningNode, 'Type unions in examples are not supported for java'); return fallback; } diff --git a/packages/jsii-rosetta/lib/renderer.ts b/packages/jsii-rosetta/lib/renderer.ts index 24ccd1f77e..2f54758105 100644 --- a/packages/jsii-rosetta/lib/renderer.ts +++ b/packages/jsii-rosetta/lib/renderer.ts @@ -133,10 +133,14 @@ export class AstRenderer { /** * Infer type of expression by the argument it is assigned to * + * If the type of the expression can include undefined (if the value is + * optional), `undefined` will be removed from the union. + * * (Will return undefined for object literals not unified with a declared type) */ public inferredTypeOfExpression(node: ts.Expression) { - return this.typeChecker.getContextualType(node); + const type = this.typeChecker.getContextualType(node); + return type ? this.typeChecker.getNonNullableType(type) : undefined; } /** @@ -152,6 +156,10 @@ export class AstRenderer { return this.typeChecker.getTypeFromTypeNode(node); } + public typeToString(type: ts.Type) { + return this.typeChecker.typeToString(type); + } + public report(node: ts.Node, messageText: string, category: ts.DiagnosticCategory = ts.DiagnosticCategory.Error) { this.diagnostics.push({ category, diff --git a/packages/jsii-rosetta/lib/typescript/types.ts b/packages/jsii-rosetta/lib/typescript/types.ts index 1bc1d76188..5ad88783bc 100644 --- a/packages/jsii-rosetta/lib/typescript/types.ts +++ b/packages/jsii-rosetta/lib/typescript/types.ts @@ -3,17 +3,16 @@ import * as ts from 'typescript'; import { AstRenderer } from '../renderer'; /** - * Return the OTHER type from undefined from a union, returns undefined if there is more than one + * Return the first non-undefined type from a union */ -export function typeWithoutUndefinedUnion(type: ts.Type | undefined): ts.Type | undefined { - if (!type || !type.isUnion()) { +export function firstTypeInUnion(typeChecker: ts.TypeChecker, type: ts.Type): ts.Type { + type = typeChecker.getNonNullableType(type); + + if (!type.isUnion()) { return type; } - const remaining = type.types.filter((t) => t.flags !== ts.TypeFlags.Undefined); - if (remaining.length > 1) { - return undefined; - } - return remaining[0]; + + return type.types[0]; } type BuiltInType = 'any' | 'boolean' | 'number' | 'string'; @@ -31,6 +30,27 @@ export function builtInTypeName(type: ts.Type): BuiltInType | undefined { return map[type.flags]; } +export function renderType(type: ts.Type): string { + if (type.isClassOrInterface()) { + return type.symbol.name; + } + if (type.isLiteral()) { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + return `${type.value}`; + } + return renderTypeFlags(type); +} + +export function renderTypeFlags(type: ts.Type) { + const ret = []; + for (const flag of Object.values(ts.TypeFlags)) { + if (typeof flag === 'number' && type.flags & flag) { + ret.push(ts.TypeFlags[flag]); + } + } + return ret.join(','); +} + export function parameterAcceptsUndefined(param: ts.ParameterDeclaration, type?: ts.Type): boolean { if (param.initializer !== undefined) { return true; @@ -90,24 +110,38 @@ export function inferMapElementType( elements: ts.NodeArray, renderer: AstRenderer, ): ts.Type | undefined { - return typeIfSame( - elements.map((el) => (ts.isPropertyAssignment(el) ? renderer.typeOfExpression(el.initializer) : undefined)), + const nodes = elements.map(elementValueNode).filter(isDefined); + const types = nodes.map((x) => renderer.typeOfExpression(x)); + + console.log( + 'inferMapElementtType', + nodes.map((x) => renderer.typeOfExpression(x)).map((x) => renderer.typeToString(x)), ); + + return types.every((t) => isSameType(types[0], t)) ? types[0] : undefined; + + function elementValueNode(el: ts.ObjectLiteralElementLike): ts.Expression | undefined { + if (ts.isPropertyAssignment(el)) { + return el.initializer; + } + if (ts.isShorthandPropertyAssignment(el)) { + return el.name; + } + return undefined; + } +} + +function isSameType(a: ts.Type, b: ts.Type) { + return a.flags === b.flags && a.symbol?.name === b.symbol?.name; } function typeIfSame(types: Array): ts.Type | undefined { - let ret: ts.Type | undefined; - for (const type of types) { - if (ret === undefined) { - ret = type; - } else { - // Not all the same - if (type !== undefined && ret.flags !== type.flags) { - return undefined; - } - } + const ttypes = types.filter(isDefined); + if (types.length === 0) { + return undefined; } - return ret; + + return ttypes.every((t) => isSameType(ttypes[0], t)) ? ttypes[0] : undefined; } /** @@ -120,3 +154,7 @@ export function arrayElementType(type: ts.Type): ts.Type | undefined { } return undefined; } + +function isDefined(x: A): x is NonNullable { + return x !== undefined; +} diff --git a/packages/jsii-rosetta/test/translations.test.ts b/packages/jsii-rosetta/test/translations.test.ts index 3e962e0793..87aa067d0a 100644 --- a/packages/jsii-rosetta/test/translations.test.ts +++ b/packages/jsii-rosetta/test/translations.test.ts @@ -15,9 +15,9 @@ import { AstHandler } from '../lib/renderer'; // // To run only the tests for a certain language you're working on, do this: // -// yarn test test/translations.test.js -t 'Translating .* to Python' -// yarn test test/translations.test.js -t 'Translating .* to Java' -// yarn test test/translations.test.js -t 'Translating .* to C#' +// yarn test test/translations.test -t 'Translating .* to Python' +// yarn test test/translations.test -t 'Translating .* to Java' +// yarn test test/translations.test -t 'Translating .* to C#' // // To narrow it down even more you can of course replace the '.*' regex with // whatever file indication you desire. diff --git a/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.cs b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.cs new file mode 100644 index 0000000000..d2d5e01ea9 --- /dev/null +++ b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.cs @@ -0,0 +1,3 @@ +new Integration(this, "Something", new IntegrationOptions { + Argument = 5 +}); diff --git a/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.java b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.java new file mode 100644 index 0000000000..20afccb743 --- /dev/null +++ b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.java @@ -0,0 +1,2 @@ +new Integration(this, "Something", new IntegrationOptions() + .argument(5)); diff --git a/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.py b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.py new file mode 100644 index 0000000000..f13c7d69a7 --- /dev/null +++ b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.py @@ -0,0 +1,3 @@ +Integration(self, "Something", + argument=5 +) \ No newline at end of file diff --git a/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.ts b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.ts new file mode 100644 index 0000000000..a9725bfdc1 --- /dev/null +++ b/packages/jsii-rosetta/test/translations/structs/struct_starting_with_i.ts @@ -0,0 +1,9 @@ +/// !hide +class Integration { + constructor(_something: any, id: string, props?: IntegrationOptions) { } +} +interface IntegrationOptions { readonly argument: number } +/// !show +new Integration(this, 'Something', { + argument: 5 +}); \ No newline at end of file