Skip to content

Latest commit

Β 

History

History
671 lines (553 loc) Β· 85.9 KB

README.md

File metadata and controls

671 lines (553 loc) Β· 85.9 KB

jsii

Join the chat at https://cdk.Dev All Contributors Build Status npm

Overview

jsii-rosetta translates code samples contained in jsii libraries from TypeScript to supported jsii target languages. This is what enables the AWS Cloud Development Kit to deliver polyglot documentation from a single codebase!

jsii-rosetta leverages knowledge about jsii language translation conventions in order to produce translations. It only supports a limited set of TypeScript language features (which can be reliably represented in other languages).

❓ Documentation

Head over to our documentation website!

The jsii toolchain spreads out on multiple repositories:

  • aws/jsii-compiler is where the jsii compiler is maintained (except releases in the 1.x line)
  • aws/jsii-rosetta is where the jsii-rosetta sample code transliteration tool is maintained (except releases in the 1.x line)
  • aws/jsii is where the rest of the toolchain is maintained, including:
    • @jsii/spec, the package that defines the .jsii assembly specification
    • jsii-config, an interactive tool to help configure your jsii package
    • jsii-pacmak, the bindings generator for jsii packages
    • jsii-reflect, a higher-level way to process .jsii assemblies
    • The jsii runtime libraries for the supported jsii target languages
    • 1.x release lines of jsii and jsii-rosetta

βš™οΈ Maintenance & Support

The applicable Maintenance & Support policy can be reviewed in SUPPORT.md.

The current status of jsii-rosetta releases is:

Release Status EOS Comment
5.6.x Current TBD npm
5.5.x Maintenance 2025-05-15 npm
5.4.x Maintenance 2025-02-28 npm

βš™οΈ Contributing

See CONTRIBUTING.

πŸŽ’ Getting Started

Rosetta for example authors

This section describes what to pay attention to when writing examples that will be converted by Rosetta.

Making examples compile

The translator can translate both code that completely compiles and typechecks, as well as code that doesn't.

In case of non-compiling samples the translations will be based off of grammatical parsing only. This has the downside that we do not have the type information available to the exact thing in all instances. Specifically struct types will not be able to be inferred from object literals. Have a look at the following piece of code:

someObject.someMethod('foo', {
  bar: 3,
});

In non-TypeScript languages, it is important to know the type of the second argument to the method here. However, without access to the definition of someMethod(), it's impossible for Rosetta to know the type, and hence it cannot translate the example. It is therefore important to include necessary imports, variable declarations, etc, to give Rosetta enough information to figure out what's going on in this code, and the example should read like this:

import * as myLib from 'some-library';

declare const someObject: myLib.SomeClass;

someObject.someMethod('foo', {
  bar: 3,
});

Enforcing correct examples

By default, Rosetta will accept non-compiling examples. If you set jsiiRosetta.strict to true in your package.json, the Rosetta command will fail if any example contains an error:

/// package.json
{
  "jsiiRosetta": {
    "strict": true
  }
}

Fixtures

To avoid having to repeat common setup every time, code samples can use "fixtures": a source template where the example is inserted. A fixture must contain the text /// here and typically looks like this:

const * as module from '@some/dependency';

class MyClass {
  constructor() {
    const obj = new MyObject();

    /// here
  }
}

The example will be inserted at the location marked as /// here and will have access to module, obj and this. Any import statements found in the example will automatically be hoisted at the top of the fixture, where they are guaranteed to be syntactically valid.

The default file loaded as a fixture is called rosetta/default.ts-fixture in the package directory (if it exists).

Examples can request an alternative fixture by specifying a fixture parameter as part of the code block fence:

```ts fixture=some-fixture

Or opt out of using the default fixture by specifying nofixture:

```ts nofixture

To specify fixtures in an @example block, use an accompanying @exampleMetadata tag:

/**
 * My cool class
 *
 * @exampleMetadata fixture=with-setup
 * @example
 *
 * new MyCoolClass();
 */

Dependencies

When compiling examples, Rosetta will make sure your package itself and all of its dependencies and peerDependencies are available in the dependency closure that your examples will be compiled in.

If there are packages you want to use in an example that should not be part of your package's dependencies, declare them in jsiiRosetta.exampleDependencies in your package.json:

/// package.json
{
  "jsiiRosetta": {
    "exampleDependencies": {
      "@some-other/package": "^1.2.3",
      "@yet-another/package": "*",
    }
  }
}

You can also set up a directory with correct dependencies yourself, and pass --directory when running jsii-rosetta extract. We recommend using the automatic closure building mechanism and specifying exampleDependencies though.

Rosetta for package publishers

This section describes how Rosetta integrates into your build process.

Extract

Rosetta has a number of subcommands. The most important one is jsii-rosetta extract.

The jsii-rosetta extract command will take one or more jsii assemblies, extract the snippets from them, will try to compile them with respect to a given home directory, and finally store all translations in something called a "tablet".

A couple of things to note here:

  • Snippets are always read from the jsii assembly. That means if you make changes to examples in source files, you must first re-run jsii to regenerate the assembly, before re-running jsii-rosetta extract.
  • The compilation directory will be used to resolve imports. Currently, you are responsible for building a directory with the correct node_modules directories in there so that a TypeScript compilation step will find all libraries referenced in the examples. This is especially revelant if your examples include libraries that depend on the current library: it is not uncommon to write examples in library A showing how to use it in combination with library B, where B depends on A. However, since by definition B cannot be in the set of dependencies of A, you must build a directory with both B and A in it somewhere in your filesystem and run Rosetta in that directory.
  • "Extract" will compile samples in parallel. The more assemblies you give it at the same time, the more efficient of a job it will be able to do.

The extract command will write a file named .jsii.tabl.json next to every assembly, containing translations for all samples found in the assembly. You should include this file in your NPM package when you publish, so that downstream consumers of the package have access to the translations.

An example invocation of jsii-rosetta extract looks like this:

jsii-rosetta extract --directory some/dir $(find . -name .jsii)

Running in parallel

Since TypeScript compilation takes a lot of time, much time can be gained by using the CPUs in your system effectively. jsii-rosetta extract will run the compilations in parallel.

jsii-rosetta will use a number of workers equal to half the number of CPU cores, up to a maximum of 16 workers. This default maximum can be overridden by setting the JSII_ROSETTA_MAX_WORKER_COUNT environment variable.

If you get out of memory errors running too many workers, run a command like this to raise the memory allowed for your workers:

/sbin/sysctl -w vm.max_map_count=2251954

Caching

Rosetta extract will translate all examples found in .jsii and write the translations to .jsii.tabl.json. From compilation to compilation, many of these examples won't have changed. Since TypeScript compilation is a fairly expensive process, we would like to avoid doing unnecessary work as much as possible.

To that end, rosetta can reuse translations from a cache, and write new translations into the same cache:

jsii-rosetta extract \
  --directory some/dir \
  --cache cache.json \
  [--trim-cache] \
  $(find . -name .jsii)

The --trim-cache flag will remove any old translations from the cache that don't exist anymore in any of the given assemblies. This prevents the cache from growing endlessly over time (an equivalent jsii-rosetta trim-cache command is available if your workflow involves running extract in multiple distinct invocations and want to retain the cache between them).

Infuse

The jsii-rosetta infuse command increases the coverage of examples for classes in the assembly.

It finds classes in the assembly that don't have an example associated with them yet (as specified via the @example tag in the doc comments), but that are used in another example found elsewhereβ€”in either a README or an example of another classβ€”it will copy the example to all classes involved. This will make sure your handwritten examples go as far as possible.

Note that in order to do this, infuse will modify the assemblies it is given.

rosetta infuse depends on the analysis perfomed by rosetta extract, and must therefore be run after extract. It can also be run as part of extract, by passing the --infuse flag:

jsii-rosetta extract \
  --directory some/dir \
  --infuse \
  $(find . -name .jsii)

Translations and pacmak

jsii-pacmak will read translation from tablets to substitute translated examples into the generated source bindings. pacmak will automatically read individual .jsii.tabl.json files if present, and can additionally also read from a global tablet file.

When a translation for a code sample cannot be found, pacmak can be configured to do one of the following:

  • Leave the sample untranslated (default)
  • Translate the sample in-place (this will slow down generation a lot, and you will not have the fine control over the compilation environment that you would have if you were to use the extract command)
  • Fail

Example:

jsii-pacmak \
  [--rosetta-tablet=global.json] \
  [--rosetta-unknown-snippets=verbatim|translate|fail]

Data flow

The diagram below shows how data flows through the jsii tools when used together:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           β”‚
β”‚  Source   β”œβ”€β”€β”€β”
β”‚           β”‚   β”‚    ╔══════════╗    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     ╔═══════════════╗    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚    β•‘          β•‘    β”‚            β”‚     β•‘    rosetta    β•‘    β”‚          β”‚
                β”œβ”€β”€β”€β–Άβ•‘   jsii   ║───▢│  assembly  │────▢║    extract    ║───▢│  tablet  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚    β•‘          β•‘    β”‚            β”‚     β•‘               β•‘    β”‚          β”‚
β”‚           β”‚   β”‚    β•šβ•β•β•β•β•β•β•β•β•β•β•    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚  README   β”‚β”€β”€β”€β”˜                           β”‚                                      β”‚
β”‚           β”‚                               β”‚                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                               β”‚           ╔═══════════════╗          β”‚
                                            β”‚           β•‘    rosetta    β•‘          β”‚
                                            └──────────▢║    infuse     β•‘β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                        β•‘               β•‘
                                                        β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
                                                                β”‚
                                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                            β”‚                                       β”‚
                                            β–Ό                                       β–Ό
                                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                     β”‚            β”‚                           β”‚          β”‚
                                     β”‚ assembly'  β”‚                           β”‚ tablet'  β”‚
                                     β”‚            β”‚                           β”‚          β”‚
                                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                            β”‚                                       β”‚
                                            β”‚                                       β”‚
                                            β”‚                                       β–Ό              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                            β”‚                               ╔═══════════════╗     β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
                                            β”‚                               β•‘               β•‘     β”‚             β”‚β”‚
                                            └──────────────────────────────▢║    pacmak     ║────▢│  packages   β”‚β”‚
                                                                            β•‘               β•‘     β”‚             β”œβ”˜
                                                                            β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                               (potentially
                                                                             live-translates)

Advanced topics

Hiding code from samples

In order to make examples compile, boilerplate code may need to be added that detracts from the example at hand (such as variable declarations and imports).

This package supports hiding parts of the original source after translation.

To mark special locations in the source tree, we can use one of three mechanisms:

  • Use a void expression statement to mark statement locations in the AST.
  • Use the comma operator combined with a void expression to mark expression locations in the AST.
  • Use special directive comments (/// !hide, /// !show) to mark locations that span AST nodes. This is less reliable (because the source location of translated syntax sometimes will have to be estimated) but the only option if you want to mark non-contiguous nodes (such as hide part of a class declaration but show statements inside the constructor).

The void expression keyword and or the comma operator feature are little-used JavaScript features that are reliably parsed by TypeScript and do not affect the semantics of the application in which they appear (so the program executes the same with or without them).

A handy mnemonic for this feature is that you can use it to "send your code into the void".

Hiding statements

Statement hiding looks like this:

before(); // will be shown

void 0; // start hiding (the argument to 'void' doesn't matter)
middle(); // will not be shown
void 'show'; // stop hiding

after(); // will be shown again

Hiding expressions

For hiding expressions, we use comma expressions to attach a void statement to an expression value without changing the meaning of the code.

Example:

foo(1, 2, (void 1, 3));

Will render as

foo(1, 2)

Also supports a visible ellipsis:

const x = (void '...', 3);

Renders to:

x = ...

Hiding across AST nodes

Use special comment directives:

before();
/// !hide
notShown();
/// !show
after();

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Aaron Costley
Aaron Costley

πŸ› πŸ’» πŸ€” πŸ‘€
Abdallah Hodieb
Abdallah Hodieb

πŸ›
Adam Ruka
Adam Ruka

πŸ› πŸ’» 🚧 πŸ‘€
Adrian Dimech
Adrian Dimech

πŸ’»
Adrian Hesketh
Adrian Hesketh

πŸ’»
Alex Pulver
Alex Pulver

πŸ›
Amazon GitHub Automation
Amazon GitHub Automation

πŸ’»
Andi Pabst
Andi Pabst

πŸ›
Andrew Wason
Andrew Wason

πŸ› πŸ’»
Andy Slezak
Andy Slezak

πŸ’»
Ansgar Mertens
Ansgar Mertens

🚧 πŸ’» πŸ›
Anshul Guleria
Anshul Guleria

πŸ€”
Ari Palo
Ari Palo

πŸ€”
Armaan Tobaccowalla
Armaan Tobaccowalla

πŸ›
BartΕ‚omiej Jurek
BartΕ‚omiej Jurek

πŸ›
Ben Bridts
Ben Bridts

πŸ“–
Ben Chaimberg
Ben Chaimberg

πŸ“–
Ben Farr
Ben Farr

πŸ“–
Ben Walters
Ben Walters

πŸ€”
Benjamin Macher
Benjamin Macher

πŸ“–
Benjamin Maizels
Benjamin Maizels

πŸ’» πŸ‘€
Bervianto Leo Pratama
Bervianto Leo Pratama

🚧
Bill Cauchois
Bill Cauchois

πŸ€”
Brecht Verhoeve
Brecht Verhoeve

πŸ€”
Breland Miley
Breland Miley

πŸ’»
CaerusKaru
CaerusKaru

πŸ’» 🚧
Calvin Combs
Calvin Combs

πŸ’» πŸ‘€
Camilo BermΓΊdez
Camilo BermΓΊdez

πŸ›
Campion Fellin
Campion Fellin

πŸ’»
Carter Van Deuren
Carter Van Deuren

πŸ›
Chris Garvis
Chris Garvis

πŸ“–
Christian Moore
Christian Moore

πŸ›
Christophe Vico
Christophe Vico

πŸ›
Christopher Currie
Christopher Currie

πŸ’» πŸ€”
Christopher Rybicki
Christopher Rybicki

πŸ“– πŸ› πŸ’»
CommanderRoot
CommanderRoot

πŸ’»
Cory Hall
Cory Hall

πŸ›
Cristian Măgherușan-Stanciu
Cristian Măgherușan-Stanciu

πŸ›
CyrusNajmabadi
CyrusNajmabadi

πŸ› πŸ€”
Damian Silbergleith
Damian Silbergleith

πŸ’» πŸ›
Daniel Dinu
Daniel Dinu

πŸ› πŸ’»
Daniel Schmidt
Daniel Schmidt

πŸ› πŸ’»
Daniel Schroeder
Daniel Schroeder

πŸ› πŸ’» πŸ“– πŸ€” 🚧
Dave Slotnick
Dave Slotnick

πŸ›
David Bell
David Bell

πŸ’»
Donald Stufft
Donald Stufft

πŸ› πŸ’» πŸ€” πŸ‘€
Dongie Agnir
Dongie Agnir

πŸ’» πŸ‘€
Eduardo Rabelo
Eduardo Rabelo

πŸ“–
Eduardo Sena S. Rosa
Eduardo Sena S. Rosa

πŸ›
Elad Ben-Israel
Elad Ben-Israel

πŸ› πŸ’» πŸ€” 🚧 πŸ‘€ πŸ“’
Eli Polonsky
Eli Polonsky

πŸ› πŸ’» πŸ€” 🚧 πŸ‘€
Eric Z. Beard
Eric Z. Beard

πŸ“†
Erik Karlsson
Erik Karlsson

πŸ›
Eugene Kozlov
Eugene Kozlov

πŸ’»
Fabio Gentile
Fabio Gentile

πŸ›
Florian Eitel
Florian Eitel

πŸ€”
Glib Shpychka
Glib Shpychka

πŸ›
Graham Lea
Graham Lea

πŸ€” πŸ‘€
Greg Lucas
Greg Lucas

πŸ’»
Hamza Assyad
Hamza Assyad

πŸ› πŸ’» πŸ€” πŸ‘€
Hari Pachuveetil
Hari Pachuveetil

πŸ“ πŸ“–
Hsing-Hui Hsu
Hsing-Hui Hsu

πŸ’» πŸ“– πŸ€” πŸ‘€
Ikko Ashimine
Ikko Ashimine

πŸ“–
James
James

πŸ› πŸ’»
James Kelley
James Kelley

πŸ›
James Mead
James Mead

πŸ’»
James Siri
James Siri

πŸ’» 🚧
Jason Del Ponte
Jason Del Ponte

πŸ€” πŸ‘€
Jason Fulghum
Jason Fulghum

πŸ€” πŸ“† πŸ‘€
Jeff Malins
Jeff Malins

πŸ’»
Jerry Kindall
Jerry Kindall

πŸ“– πŸ€”
Jimmy Gaussen
Jimmy Gaussen

πŸ€”
Johannes Weber
Johannes Weber

πŸ“–
John Pantzlaff
John Pantzlaff

πŸ’»
Jon Steinich
Jon Steinich

πŸ› πŸ€” πŸ’»
Joseph Lawson
Joseph Lawson

πŸ‘€
Joseph Martin
Joseph Martin

πŸ›
Junix
Junix

πŸ›
Justin Frahm
Justin Frahm

πŸ›
Justin Taylor
Justin Taylor

πŸ›
Kaizen Conroy
Kaizen Conroy

πŸ’» πŸ›
Kaizen Conroy
Kaizen Conroy

πŸ’»
Kaushik Borra
Kaushik Borra

πŸ›
Kendra Neil
Kendra Neil

πŸ’»
Khurram Jalil
Khurram Jalil

πŸ“–
Knut O. Hellan
Knut O. Hellan

πŸ›
Kyle Thomson
Kyle Thomson

πŸ’» πŸ‘€
Leandro Padua
Leandro Padua

πŸ›
Liang Zhou
Liang Zhou

πŸ› πŸ’»
Madeline Kusters
Madeline Kusters

πŸ’» πŸ›
Maja S Bratseth
Maja S Bratseth

πŸ›
Marcos Diez
Marcos Diez

πŸ›
Mark Nielsen
Mark Nielsen

πŸ’»
Matthew Bonig
Matthew Bonig

πŸ› πŸ“
Matthew Pirocchi
Matthew Pirocchi

πŸ’» πŸ€” πŸ‘€
Meng Xin Zhu
Meng Xin Zhu

πŸ›
Michael Neil
Michael Neil

🚧
Mike Lane
Mike Lane

πŸ›
Mitch Garnaat
Mitch Garnaat

πŸ› πŸ’» πŸ€” πŸ‘€
Mitchell Valine
Mitchell Valine

πŸ› πŸ’» πŸ€” 🚧 πŸ‘€
Mohamad Soufan
Mohamad Soufan

πŸ“–
Momo Kornher
Momo Kornher

πŸ’»
Mykola Mogylenko
Mykola Mogylenko

πŸ›
Naumel
Naumel

πŸ‘€
Neta Nir
Neta Nir

πŸ’» πŸ€” 🚧 πŸ‘€
Nick Lynch
Nick Lynch

πŸ› πŸ’» 🚧 πŸ‘€
Niranjan Jayakar
Niranjan Jayakar

πŸ› πŸ’» πŸ€” 🚧 πŸ‘€
Noah Litov
Noah Litov

πŸ’» 🚧 πŸ‘€
Otavio Macedo
Otavio Macedo

πŸ’» πŸ›
PIDZ - Bart
PIDZ - Bart

πŸ€”
Peter Woodworth
Peter Woodworth

🚧
Petr Kacer
Petr Kacer

πŸ›
Petra Barus
Petra Barus

πŸ’»
Philip Cali
Philip Cali

πŸ€”
Quentin Loos
Quentin Loos

πŸ€”
Raphael
Raphael

πŸ›
Richard H Boyd
Richard H Boyd

πŸ›
Rico Huijbers
Rico Huijbers

πŸ› πŸ’» πŸ€” 🚧 πŸ‘€
Romain Marcadier
Romain Marcadier

πŸ› πŸ’» 🎨 πŸ€” 🚧 πŸ‘€ πŸ“
SADIK KUZU
SADIK KUZU

πŸ‘€
SK
SK

πŸ€”
Sam Fink
Sam Fink

πŸ’» πŸ‘€
Sam Goodwin
Sam Goodwin

πŸ‘€
Sebastian Korfmann
Sebastian Korfmann

πŸ› πŸ’» πŸ€”
Sepehr Laal
Sepehr Laal

πŸ›
Shane Witbeck
Shane Witbeck

πŸ€”
Shiv Lakshminarayan
Shiv Lakshminarayan

πŸ’» 🚧 πŸ‘€
Somaya
Somaya

πŸ’» πŸ€” 🚧 πŸ‘€
Stephen Kuenzli
Stephen Kuenzli

πŸ“–
Takahiro Sugiura
Takahiro Sugiura

πŸ“–
The Gitter Badger
The Gitter Badger

πŸ’» 🚧
Thomas Poignant
Thomas Poignant

πŸ›
Thomas Steinbach
Thomas Steinbach

πŸ›
Thorsten Hoeger
Thorsten Hoeger

πŸ’»
Tim Wagner
Tim Wagner

πŸ› πŸ€”
Tobias Lidskog
Tobias Lidskog

πŸ’»
Tom Bonner
Tom Bonner

πŸ›
Ty Coghlan
Ty Coghlan

πŸ›
Tyler van Hensbergen
Tyler van Hensbergen

πŸ€”
Vlad Hrybok
Vlad Hrybok

πŸ›
Vladimir Shchur
Vladimir Shchur

πŸ›
Will Bender
Will Bender

πŸ›
Yan Zhulanow
Yan Zhulanow

πŸ’»
Yigong Liu
Yigong Liu

πŸ› πŸ€”
Zach Bienenfeld
Zach Bienenfeld

πŸ›
ajnarang
ajnarang

πŸ€”
aniljava
aniljava

πŸ’»
arnogeurts-sqills
arnogeurts-sqills

πŸ› πŸ’»
cn-cit
cn-cit

πŸ›
deccy-mcc
deccy-mcc

πŸ›
dependabot-preview[bot]
dependabot-preview[bot]

πŸ› 🚧
dependabot[bot]
dependabot[bot]

🚧
dheffx
dheffx

πŸ›
gregswdl
gregswdl

πŸ›
guyroberts21
guyroberts21

πŸ“–
mattBrzezinski
mattBrzezinski

πŸ“–
mergify
mergify

🚧
mergify[bot]
mergify[bot]

🚧
nathannaveen
nathannaveen

🚧
seiyashima42
seiyashima42

πŸ› πŸ’» πŸ“–
sullis
sullis

πŸ’»
vaneek
vaneek

πŸ›
wendysophie
wendysophie

πŸ›

This project follows the all-contributors specification. Contributions of any kind welcome!

βš–οΈ License

jsii is distributed under the Apache License, Version 2.0.

See LICENSE and NOTICE for more information.