diff --git a/.all-contributorsrc b/.all-contributorsrc
index 86223eafbf..d9fba3b00f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1246,7 +1246,7 @@
},
{
"login": "kaizen3031593",
- "name": "kaizen3031593",
+ "name": "Kaizen Conroy",
"avatar_url": "https://avatars.githubusercontent.com/u/36202692?v=4",
"profile": "https://github.com/kaizen3031593",
"contributions": [
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 688c4518b5..2eea33c9f1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -41,7 +41,7 @@ jobs:
java-version: '8'
distribution: 'zulu'
- name: Set up Node 12
- uses: actions/setup-node@v2.4.1
+ uses: actions/setup-node@v2.5.0
with:
cache: yarn
node-version: '12'
@@ -135,7 +135,7 @@ jobs:
java-version: '8'
distribution: 'zulu'
- name: Set up Node 12
- uses: actions/setup-node@v2.4.1
+ uses: actions/setup-node@v2.5.0
with:
cache: yarn
node-version: '12'
@@ -304,7 +304,7 @@ jobs:
java-version: ${{ matrix.java }}
distribution: 'zulu'
- name: Set up Node ${{ matrix.node }}
- uses: actions/setup-node@v2.4.1
+ uses: actions/setup-node@v2.5.0
with:
cache: yarn
node-version: ${{ matrix.node }}
diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml
index 95fda35aac..c94b43b0be 100644
--- a/.github/workflows/yarn-upgrade.yml
+++ b/.github/workflows/yarn-upgrade.yml
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v2.4.0
- name: Set up Node
- uses: actions/setup-node@v2.4.1
+ uses: actions/setup-node@v2.5.0
with:
cache: yarn
node-version: 12
@@ -140,7 +140,7 @@ jobs:
title: 'chore: npm-check-updates && yarn upgrade'
body: |-
Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date.
- labels: contribution/core,dependencies
+ labels: contribution/core,dependencies,pr/auto-approve
team-reviewers: aws-cdk-team
# Privileged token so automated PR validation happens
token: ${{ secrets.AUTO_APPROVE_GITHUB_TOKEN }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45a4f4f9d4..ac9e0cd1cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,26 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [1.47.0](https://github.com/aws/jsii/compare/v1.45.0...v1.47.0) (2021-12-06)
+
+
+### Features
+
+* **reflect:** add `allTypes` accessor ([#3194](https://github.com/aws/jsii/issues/3194)) ([41f301a](https://github.com/aws/jsii/commit/41f301a8304bd1ed6ed7ec4e31bd23ffd1a2ed8b))
+* **rosetta:** metadata tag for fixtures in docs ([#3200](https://github.com/aws/jsii/issues/3200)) ([8cefa8b](https://github.com/aws/jsii/commit/8cefa8bc8c9554913960b48226531aff874ee247))
+* **rosetta:** generate rosetta tablets next to each assembly ([#3223](https://github.com/aws/jsii/issues/3223)) ([1e7b604](https://github.com/aws/jsii/commit/1e7b604c15a0083f27ceafd8ca32ff9b6cf61759))
+* **rosetta:** reuse output file as additional cache and introduce `--infuse` option for `extract` ([#3210](https://github.com/aws/jsii/issues/3210)) ([ccb3c57](https://github.com/aws/jsii/commit/ccb3c57b834225f16ec619f55a7976e59b7a53c3))
+
+
+### Bug Fixes
+
+* **jsii:** constants can't mix letters and digits ([#3209](https://github.com/aws/jsii/issues/3209)) ([a444e29](https://github.com/aws/jsii/commit/a444e2993b73dc002586e2c4a3446121c279eb65)), closes [#3208](https://github.com/aws/jsii/issues/3208)
+* **jsii:** deprecation message is not displayed for deprecated classes ([#3206](https://github.com/aws/jsii/issues/3206)) ([3841538](https://github.com/aws/jsii/commit/3841538179226b67c756ca8689ac1a3de4bec521))
+* **pacmak:** don't automatically translate examples without asking ([#3219](https://github.com/aws/jsii/issues/3219)) ([937f8c3](https://github.com/aws/jsii/commit/937f8c3753bec27269ce9213a6bc55fea79647b0))
+* **rosetta:** `extract` ignores `--compile` option ([#3193](https://github.com/aws/jsii/issues/3193)) ([639c510](https://github.com/aws/jsii/commit/639c510ba6d07b26bf35d0c8d3c9cdcced6d916a))
+* **rosetta:** enum resolution breaks for properties ([#3190](https://github.com/aws/jsii/issues/3190)) ([3b49066](https://github.com/aws/jsii/commit/3b49066e4dd960385709dbdce1d9c1fbfb2f20cf))
+* **rosetta:** use `--compile` flag by default ([#3218](https://github.com/aws/jsii/issues/3218)) ([9df7950](https://github.com/aws/jsii/commit/9df7950f263aae045877accb45007e0f9a5b03bd))
+
## [1.46.0](https://github.com/aws/jsii/compare/v1.45.0...v1.46.0) (2021-11-21)
diff --git a/README.md b/README.md
index 7a1d76ddd4..68acb33c44 100644
--- a/README.md
+++ b/README.md
@@ -139,81 +139,81 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Joseph Martin 🐛 |
Junix 🐛 |
Justin Taylor 🐛 |
+ Kaizen Conroy 💻 🐛 |
Kyle Thomson 💻 👀 |
Leandro Padua 🐛 |
Liang Zhou 🐛 💻 |
- Madeline Kusters 💻 🐛 |
+ Madeline Kusters 💻 🐛 |
Maja S Bratseth 🐛 |
Marcos Diez 🐛 |
Mark Nielsen 💻 |
Matthew Bonig 🐛 📝 |
Matthew Pirocchi 💻 🤔 👀 |
Michael Neil 🚧 |
- Mike Lane 🐛 |
+ Mike Lane 🐛 |
Mitch Garnaat 🐛 💻 🤔 👀 |
Mitchell Valine 🐛 💻 🤔 🚧 👀 |
Mohamad Soufan 📖 |
Neta Nir 💻 🤔 🚧 👀 |
Nick Lynch 🐛 💻 🚧 👀 |
Niranjan Jayakar 🐛 💻 🤔 🚧 👀 |
- Noah Litov 💻 🚧 👀 |
+ Noah Litov 💻 🚧 👀 |
Otavio Macedo 💻 🐛 |
PIDZ - Bart 🤔 |
Peter Woodworth 🚧 |
Petr Kacer 🐛 |
Petra Barus 💻 |
Philip Cali 🤔 |
- Quentin Loos 🤔 |
+ Quentin Loos 🤔 |
Raphael 🐛 |
Richard H Boyd 🐛 |
Rico Huijbers 🐛 💻 🤔 🚧 👀 |
Romain Marcadier 🐛 💻 🎨 🤔 🚧 👀 📝 |
SADIK KUZU 👀 |
SK 🤔 |
- Sam Fink 💻 👀 |
+ Sam Fink 💻 👀 |
Sam Goodwin 👀 |
Sebastian Korfmann 🐛 💻 🤔 |
Shane Witbeck 🤔 |
Shiv Lakshminarayan 💻 🚧 👀 |
Somaya 💻 🤔 🚧 👀 |
The Gitter Badger 💻 🚧 |
- Thomas Poignant 🐛 |
+ Thomas Poignant 🐛 |
Thomas Steinbach 🐛 |
Thorsten Hoeger 💻 |
Tim Wagner 🐛 🤔 |
Tobias Lidskog 💻 |
Ty Coghlan 🐛 |
Tyler van Hensbergen 🤔 |
- Vlad Hrybok 🐛 |
+ Vlad Hrybok 🐛 |
Vladimir Shchur 🐛 |
Yan Zhulanow 💻 |
Yigong Liu 🐛 🤔 |
Zach Bienenfeld 🐛 |
ajnarang 🤔 |
aniljava 💻 |
- deccy-mcc 🐛 |
+ deccy-mcc 🐛 |
dependabot-preview[bot] 🐛 🚧 |
dependabot[bot] 🚧 |
dheffx 🐛 |
gregswdl 🐛 |
guyroberts21 📖 |
- kaizen3031593 💻 🐛 |
mattBrzezinski 📖 |
diff --git a/eslint-config.yaml b/eslint-config.yaml
index 83ee363cec..8c6c72d389 100644
--- a/eslint-config.yaml
+++ b/eslint-config.yaml
@@ -238,6 +238,10 @@ rules:
'no-case-declarations': off
'require-atomic-updates': off
+ # This is not a bad rule but it got sprung on us and our code loudly fails it
+ # Disable to get the eslint upgrade to pass.
+ '@typescript-eslint/no-unsafe-argument': off
+
# 'consistent-return' actually decreases safety. Its use will enforce useless `throws`
# statements, forcing a runtime error that occlude cases where the TypeScript type
# checker would actually have caught something like a non-exhaustive `switch` statement
diff --git a/lerna.json b/lerna.json
index cd5f11c975..4b6def9308 100644
--- a/lerna.json
+++ b/lerna.json
@@ -10,5 +10,5 @@
"rejectCycles": true
}
},
- "version": "1.46.0"
+ "version": "1.47.0"
}
diff --git a/package.json b/package.json
index 90690b0f36..dde647af0e 100644
--- a/package.json
+++ b/package.json
@@ -15,23 +15,23 @@
"compliance": "(cd tools/jsii-compliance && yarn report)"
},
"devDependencies": {
- "@jest/types": "^27.2.4",
- "@typescript-eslint/eslint-plugin": "^4.33.0",
- "@typescript-eslint/parser": "^4.33.0",
+ "@jest/types": "^27.2.5",
+ "@typescript-eslint/eslint-plugin": "^5.4.0",
+ "@typescript-eslint/parser": "^5.4.0",
"all-contributors-cli": "^6.20.0",
- "eslint": "^7.32.0",
+ "eslint": "^8.3.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^2.5.0",
- "eslint-plugin-import": "^2.24.2",
+ "eslint-plugin-import": "^2.25.3",
"eslint-plugin-prettier": "^4.0.0",
- "jest-circus": "^27.2.4",
- "jest-config": "^27.2.4",
+ "jest-circus": "^27.3.1",
+ "jest-config": "^27.3.1",
"lerna": "^4.0.0",
"prettier": "^2.4.1",
- "standard-version": "^9.3.1",
- "ts-jest": "^27.0.5",
- "ts-node": "^10.2.1",
+ "standard-version": "^9.3.2",
+ "ts-jest": "^27.0.7",
+ "ts-node": "^10.4.0",
"typescript": "~3.9.10"
},
"repository": {
diff --git a/packages/@jsii/Directory.Build.targets b/packages/@jsii/Directory.Build.targets
index 5df07af12a..b2b6ca0bf2 100644
--- a/packages/@jsii/Directory.Build.targets
+++ b/packages/@jsii/Directory.Build.targets
@@ -6,7 +6,7 @@
-
+
diff --git a/packages/@jsii/check-node/package.json b/packages/@jsii/check-node/package.json
index 1ec84558d8..e553956449 100644
--- a/packages/@jsii/check-node/package.json
+++ b/packages/@jsii/check-node/package.json
@@ -41,8 +41,8 @@
},
"devDependencies": {
"@types/chalk": "^2.2.0",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "jest": "^27.2.4"
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "jest": "^27.3.1"
}
}
diff --git a/packages/@jsii/dotnet-runtime-test/package.json b/packages/@jsii/dotnet-runtime-test/package.json
index 8680f8820e..ccf4e8654f 100644
--- a/packages/@jsii/dotnet-runtime-test/package.json
+++ b/packages/@jsii/dotnet-runtime-test/package.json
@@ -31,7 +31,7 @@
},
"devDependencies": {
"@jsii/dotnet-runtime": "^0.0.0",
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"jsii-calc": "^3.20.120",
"jsii-pacmak": "^0.0.0",
"typescript": "~3.9.10"
diff --git a/packages/@jsii/dotnet-runtime/package.json b/packages/@jsii/dotnet-runtime/package.json
index 3419d0d8e1..33aca99fb0 100644
--- a/packages/@jsii/dotnet-runtime/package.json
+++ b/packages/@jsii/dotnet-runtime/package.json
@@ -39,8 +39,8 @@
},
"devDependencies": {
"@jsii/runtime": "^0.0.0",
- "@types/node": "^12.20.28",
- "@types/semver": "^7.3.8",
+ "@types/node": "^12.20.37",
+ "@types/semver": "^7.3.9",
"jsii-build-tools": "^0.0.0",
"semver": "^7.3.5",
"typescript": "~3.9.10"
diff --git a/packages/@jsii/go-runtime/package.json b/packages/@jsii/go-runtime/package.json
index 96a5f2216b..92c125dfd0 100644
--- a/packages/@jsii/go-runtime/package.json
+++ b/packages/@jsii/go-runtime/package.json
@@ -24,14 +24,14 @@
},
"devDependencies": {
"@types/fs-extra": "^9.0.13",
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"codemaker": "^0.0.0",
- "eslint": "^7.32.0",
+ "eslint": "^8.3.0",
"fs-extra": "^9.1.0",
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
"prettier": "^2.4.1",
- "ts-node": "^10.2.1",
+ "ts-node": "^10.4.0",
"typescript": "~3.9.10"
}
}
diff --git a/packages/@jsii/integ-test/package.json b/packages/@jsii/integ-test/package.json
index a60a591de5..55e57cf026 100644
--- a/packages/@jsii/integ-test/package.json
+++ b/packages/@jsii/integ-test/package.json
@@ -17,10 +17,10 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@octokit/rest": "^18.11.4",
+ "@octokit/rest": "^18.12.0",
"dotenv": "^8.6.0",
"fs-extra": "^9.1.0",
- "jest": "^27.2.4",
+ "jest": "^27.3.1",
"jsii": "^0.0.0",
"jsii-pacmak": "^0.0.0",
"jsii-rosetta": "^0.0.0",
@@ -29,10 +29,10 @@
"devDependencies": {
"@types/dotenv": "^8.2.0",
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "@types/tar": "^4.0.5",
- "eslint": "^7.32.0",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "@types/tar": "^6.1.1",
+ "eslint": "^8.3.0",
"prettier": "^2.4.1",
"typescript": "~3.9.10"
}
diff --git a/packages/@jsii/java-runtime/package.json b/packages/@jsii/java-runtime/package.json
index 45446ce3c8..cf74f3fbfa 100644
--- a/packages/@jsii/java-runtime/package.json
+++ b/packages/@jsii/java-runtime/package.json
@@ -33,7 +33,7 @@
},
"devDependencies": {
"@jsii/runtime": "^0.0.0",
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"jsii-build-tools": "^0.0.0",
"typescript": "~3.9.10"
}
diff --git a/packages/@jsii/kernel/package.json b/packages/@jsii/kernel/package.json
index 3a7c862a95..eecf968f32 100644
--- a/packages/@jsii/kernel/package.json
+++ b/packages/@jsii/kernel/package.json
@@ -39,16 +39,16 @@
"@scope/jsii-calc-base": "^0.0.0",
"@scope/jsii-calc-lib": "^0.0.0",
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "@types/tar": "^4.0.5",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "@types/tar": "^6.1.1",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jest-expect-message": "^1.0.2",
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
"prettier": "^2.4.1",
- "ts-jest": "^27.0.5",
+ "ts-jest": "^27.0.7",
"typescript": "~3.9.10"
}
}
diff --git a/packages/@jsii/python-runtime/package.json b/packages/@jsii/python-runtime/package.json
index f3f9a36e97..62a2669f42 100644
--- a/packages/@jsii/python-runtime/package.json
+++ b/packages/@jsii/python-runtime/package.json
@@ -41,7 +41,7 @@
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
"jsii-pacmak": "^0.0.0",
- "ts-node": "^10.2.1",
+ "ts-node": "^10.4.0",
"typescript": "~3.9.10"
}
}
diff --git a/packages/@jsii/python-runtime/requirements.txt b/packages/@jsii/python-runtime/requirements.txt
index 825f29a79d..8325b38ea2 100644
--- a/packages/@jsii/python-runtime/requirements.txt
+++ b/packages/@jsii/python-runtime/requirements.txt
@@ -1,9 +1,9 @@
-black~=21.10b0
+black~=21.11b1
mypy==0.812
pip~=21.3
pytest~=6.2
pytest-mypy~=0.8
-setuptools~=58.5
+setuptools~=59.2
wheel~=0.37
-e .
diff --git a/packages/@jsii/python-runtime/setup.py b/packages/@jsii/python-runtime/setup.py
index 52768e51c6..8a569b8e82 100644
--- a/packages/@jsii/python-runtime/setup.py
+++ b/packages/@jsii/python-runtime/setup.py
@@ -35,7 +35,7 @@
"cattrs~=1.8.0 ; python_version >= '3.7'",
"importlib_resources ; python_version < '3.7'",
"python-dateutil",
- "typing_extensions~=3.7",
+ "typing_extensions>=3.7,<5.0",
],
python_requires="~=3.6",
classifiers=[
diff --git a/packages/@jsii/runtime/package.json b/packages/@jsii/runtime/package.json
index 3db822ffc7..8d337546d7 100644
--- a/packages/@jsii/runtime/package.json
+++ b/packages/@jsii/runtime/package.json
@@ -41,17 +41,17 @@
"devDependencies": {
"@scope/jsii-calc-base": "^0.0.0",
"@scope/jsii-calc-lib": "^0.0.0",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
"prettier": "^2.4.1",
"source-map-loader": "^3.0.0",
- "ts-jest": "^27.0.5",
+ "ts-jest": "^27.0.7",
"typescript": "~3.9.10",
- "webpack": "^5.57.1",
- "webpack-cli": "^4.8.0"
+ "webpack": "^5.64.3",
+ "webpack-cli": "^4.9.1"
}
}
diff --git a/packages/@jsii/spec/lib/validate-assembly.ts b/packages/@jsii/spec/lib/validate-assembly.ts
index 250c6ae478..4f798e2f97 100644
--- a/packages/@jsii/spec/lib/validate-assembly.ts
+++ b/packages/@jsii/spec/lib/validate-assembly.ts
@@ -8,7 +8,7 @@ export const schema: Schema = require('../schema/jsii-spec.schema.json');
export function validateAssembly(obj: any): Assembly {
const validator = new Validator();
validator.addSchema(schema); // For definitions
- const result = validator.validate(obj, schema, { nestedErrors: true } as any); // nestedErrors does exist but is not in the TypeScript definitions
+ const result = validator.validate(obj, schema, { nestedErrors: true });
if (result.valid) {
return obj;
}
diff --git a/packages/@jsii/spec/package.json b/packages/@jsii/spec/package.json
index bbb2eaa02a..3e7a5ef946 100644
--- a/packages/@jsii/spec/package.json
+++ b/packages/@jsii/spec/package.json
@@ -34,13 +34,13 @@
"jsonschema": "^1.4.0"
},
"devDependencies": {
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jsii-build-tools": "^0.0.0",
"prettier": "^2.4.1",
"typescript": "~3.9.10",
- "typescript-json-schema": "^0.51.0"
+ "typescript-json-schema": "^0.52.0"
}
}
diff --git a/packages/@scope/jsii-calc-base-of-base/package.json b/packages/@scope/jsii-calc-base-of-base/package.json
index 3c33eed5ef..b4ed0b7c0d 100644
--- a/packages/@scope/jsii-calc-base-of-base/package.json
+++ b/packages/@scope/jsii-calc-base-of-base/package.json
@@ -30,7 +30,7 @@
"test:update": "npm run build && UPDATE_DIFF=1 npm run test"
},
"devDependencies": {
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-rosetta": "^0.0.0",
diff --git a/packages/@scope/jsii-calc-base/package.json b/packages/@scope/jsii-calc-base/package.json
index 13407f9701..0398e3d2c0 100644
--- a/packages/@scope/jsii-calc-base/package.json
+++ b/packages/@scope/jsii-calc-base/package.json
@@ -35,7 +35,7 @@
"@scope/jsii-calc-base-of-base": "^2.1.1"
},
"devDependencies": {
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-rosetta": "^0.0.0",
diff --git a/packages/@scope/jsii-calc-lib/package.json b/packages/@scope/jsii-calc-lib/package.json
index 6a9659d57b..e068cebef4 100644
--- a/packages/@scope/jsii-calc-lib/package.json
+++ b/packages/@scope/jsii-calc-lib/package.json
@@ -39,7 +39,7 @@
"@scope/jsii-calc-base-of-base": "^2.1.1"
},
"devDependencies": {
- "@types/node": "^12.20.28",
+ "@types/node": "^12.20.37",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-rosetta": "^0.0.0",
diff --git a/packages/codemaker/package.json b/packages/codemaker/package.json
index fcf27a2e04..960ad07123 100644
--- a/packages/codemaker/package.json
+++ b/packages/codemaker/package.json
@@ -31,16 +31,16 @@
"package": "rm -fr dist/js && mkdir -p dist/js && mv $(npm pack) dist/js"
},
"dependencies": {
- "camelcase": "^6.2.0",
+ "camelcase": "^6.2.1",
"decamelize": "^5.0.1",
"fs-extra": "^9.1.0"
},
"devDependencies": {
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"prettier": "^2.4.1",
"typescript": "~3.9.10"
}
diff --git a/packages/jsii-calc/lib/calculator.ts b/packages/jsii-calc/lib/calculator.ts
index 23cea54d00..f6cbdccab5 100644
--- a/packages/jsii-calc/lib/calculator.ts
+++ b/packages/jsii-calc/lib/calculator.ts
@@ -168,7 +168,7 @@ export namespace composition {
* The expression that this operation consists of.
* Must be implemented by derived classes.
*/
- abstract readonly expression: NumericValue;
+ public abstract readonly expression: NumericValue;
public toString() {
switch (this.stringStyle) {
diff --git a/packages/jsii-calc/package.json b/packages/jsii-calc/package.json
index d1c77df2a1..16fea36110 100644
--- a/packages/jsii-calc/package.json
+++ b/packages/jsii-calc/package.json
@@ -51,8 +51,8 @@
"@scope/jsii-calc-lib": "^0.0.0"
},
"devDependencies": {
- "@types/node": "^12.20.28",
- "eslint": "^7.32.0",
+ "@types/node": "^12.20.37",
+ "eslint": "^8.3.0",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-rosetta": "^0.0.0",
diff --git a/packages/jsii-config/package.json b/packages/jsii-config/package.json
index 58f7d71ad7..c3ca077367 100644
--- a/packages/jsii-config/package.json
+++ b/packages/jsii-config/package.json
@@ -20,11 +20,11 @@
},
"devDependencies": {
"@types/inquirer": "^8.1.3",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "@types/yargs": "^17.0.3",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "@types/yargs": "^17.0.7",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jest-expect-message": "^1.0.2",
"prettier": "^2.4.1",
"typescript": "~3.9.10"
diff --git a/packages/jsii-diff/package.json b/packages/jsii-diff/package.json
index fe82688389..d28555edd9 100644
--- a/packages/jsii-diff/package.json
+++ b/packages/jsii-diff/package.json
@@ -43,11 +43,11 @@
},
"devDependencies": {
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
"@types/tar-fs": "^2.0.1",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jest-expect-message": "^1.0.2",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
diff --git a/packages/jsii-pacmak/bin/jsii-pacmak.ts b/packages/jsii-pacmak/bin/jsii-pacmak.ts
index 35f39df32d..189ea7a9e2 100644
--- a/packages/jsii-pacmak/bin/jsii-pacmak.ts
+++ b/packages/jsii-pacmak/bin/jsii-pacmak.ts
@@ -95,7 +95,7 @@ import { VERSION_DESC } from '../lib/version';
.option('rosetta-translate-live', {
type: 'boolean',
desc: "Translate code samples on-the-fly if they can't be found in the samples tablet (deprecated)",
- default: true,
+ default: undefined,
})
.option('rosetta-unknown-snippets', {
type: 'string',
@@ -135,6 +135,21 @@ import { VERSION_DESC } from '../lib/version';
// Default to 4 threads in case of concurrency, good enough for most situations
debug('command line arguments:', argv);
+ if (
+ argv['rosetta-translate-live'] !== undefined &&
+ argv['rosetta-unknown-snippets'] !== undefined
+ ) {
+ throw new Error(
+ 'Prefer using --rosetta-unknown-snippets over --rosetta-translate-live',
+ );
+ }
+
+ const rosettaUnknownSnippets =
+ (argv['rosetta-unknown-snippets'] as UnknownSnippetMode | undefined) ??
+ (argv['rosetta-translate-live']
+ ? UnknownSnippetMode.TRANSLATE
+ : UnknownSnippetMode.VERBATIM);
+
return pacmak({
argv,
clean: argv.clean,
@@ -147,10 +162,7 @@ import { VERSION_DESC } from '../lib/version';
outputDirectory: argv.outdir,
parallel: argv.parallel,
recurse: argv.recurse,
- rosettaLiveConversion: argv['rosetta-translate-live'],
- rosettaUnknownSnippets: argv['rosetta-unknown-snippets'] as
- | UnknownSnippetMode
- | undefined,
+ rosettaUnknownSnippets,
rosettaTablet: argv['rosetta-tablet'],
targets: argv.targets?.map((target) => target as TargetName),
updateNpmIgnoreFiles: argv.npmignore,
diff --git a/packages/jsii-pacmak/lib/index.ts b/packages/jsii-pacmak/lib/index.ts
index 5d3c358306..1e8d6d7e9f 100644
--- a/packages/jsii-pacmak/lib/index.ts
+++ b/packages/jsii-pacmak/lib/index.ts
@@ -29,7 +29,6 @@ export async function pacmak({
outputDirectory,
parallel = true,
recurse = false,
- rosettaLiveConversion,
rosettaTablet,
targets = Object.values(TargetName),
timers = new Timers(),
@@ -37,13 +36,7 @@ export async function pacmak({
updateNpmIgnoreFiles = false,
validateAssemblies = false,
}: PacmakOptions): Promise {
- const unknownSnippets =
- rosettaUnknownSnippets ??
- (rosettaLiveConversion
- ? UnknownSnippetMode.TRANSLATE
- : UnknownSnippetMode.VERBATIM);
-
- const rosetta = new Rosetta({ unknownSnippets });
+ const rosetta = new Rosetta({ unknownSnippets: rosettaUnknownSnippets });
if (rosettaTablet) {
await rosetta.loadTabletFromFile(rosettaTablet);
}
@@ -236,19 +229,10 @@ export interface PacmakOptions {
*/
readonly recurse?: boolean;
- /**
- * Whether `jsii-rosetta` conversion should be performed in-band for examples found in documentation which are not
- * already translated in the `rosettaTablet` file.
- *
- * @default false
- * @deprecated Use `rosettaUnknownSnippets` instead.
- */
- readonly rosettaLiveConversion?: boolean;
-
/**
* How rosetta should treat snippets that cannot be loaded from a translation tablet.
*
- * @default - falls back to the default of `rosettaLiveConversion`.
+ * @default UnknownSnippetMode.VERBATIM
*/
readonly rosettaUnknownSnippets?: UnknownSnippetMode;
diff --git a/packages/jsii-pacmak/lib/targets/python/requirements-dev.txt b/packages/jsii-pacmak/lib/targets/python/requirements-dev.txt
index ff95e1d96e..b2dc5a9e05 100644
--- a/packages/jsii-pacmak/lib/targets/python/requirements-dev.txt
+++ b/packages/jsii-pacmak/lib/targets/python/requirements-dev.txt
@@ -3,7 +3,7 @@
# be installed in the virtual environment used for building the distribution
# package (wheel, sdist), but not declared as build-system dependencies.
-setuptools~=58.5.3 # build-system
+setuptools~=59.1.1 # build-system
wheel~=0.37.0 # build-system
-twine~=3.5.0
+twine~=3.6.0
diff --git a/packages/jsii-pacmak/package.json b/packages/jsii-pacmak/package.json
index 5163ba831c..493492d406 100644
--- a/packages/jsii-pacmak/package.json
+++ b/packages/jsii-pacmak/package.json
@@ -59,16 +59,16 @@
"@types/clone": "^2.1.1",
"@types/commonmark": "^0.27.5",
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "@types/semver": "^7.3.8",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "@types/semver": "^7.3.9",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
"prettier": "^2.4.1",
- "ts-jest": "^27.0.5",
+ "ts-jest": "^27.0.7",
"typescript": "~3.9.10"
},
"keywords": [
diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap
index c4f1ca905f..bca0c45aaf 100644
--- a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap
+++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap
@@ -1147,7 +1147,7 @@ testpkg.FooBar=example.test.demo.FooBar
exports[`diamond-struct-parameter.ts: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -2447,7 +2447,7 @@ testpkg.Namespace2.Foo.Final=example.test.demo.Namespace2$Foo.Final
exports[`nested-types.ts: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/prerelease-identifiers.test.ts.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/prerelease-identifiers.test.ts.snap
index cf54ff1ed1..e4cda418d9 100644
--- a/packages/jsii-pacmak/test/generated-code/__snapshots__/prerelease-identifiers.test.ts.snap
+++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/prerelease-identifiers.test.ts.snap
@@ -416,7 +416,7 @@ foo
exports[`foo@1.2.3 depends on bar@^2.0.0-rc.42: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -925,7 +925,7 @@ foo
exports[`foo@1.2.3 depends on bar@^4.5.6-pre.1337: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -1414,7 +1414,7 @@ foo
exports[`foo@2.0.0-rc.42: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -1900,7 +1900,7 @@ foo
exports[`foo@4.5.6-pre.1337: /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.ts.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.ts.snap
index c75ddb6185..ed2a5b917b 100644
--- a/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.ts.snap
+++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.ts.snap
@@ -243,7 +243,7 @@ scope.jsii-calc-base
exports[`Generated code for "@scope/jsii-calc-base": /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -723,7 +723,7 @@ scope.jsii-calc-base-of-base
exports[`Generated code for "@scope/jsii-calc-base-of-base": /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -1176,7 +1176,7 @@ scope.jsii-calc-lib
exports[`Generated code for "@scope/jsii-calc-lib": /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
@@ -2408,7 +2408,7 @@ foo = "bar"
exports[`Generated code for "jsii-calc": /python/pyproject.toml 1`] = `
[build-system]
-requires = ["setuptools~=58.5.3", "wheel~=0.37.0"]
+requires = ["setuptools~=59.1.1", "wheel~=0.37.0"]
build-backend = "setuptools.build_meta"
`;
diff --git a/packages/jsii-reflect/lib/assembly.ts b/packages/jsii-reflect/lib/assembly.ts
index 08f3361258..f732a3a92f 100644
--- a/packages/jsii-reflect/lib/assembly.ts
+++ b/packages/jsii-reflect/lib/assembly.ts
@@ -169,6 +169,20 @@ export class Assembly extends ModuleLike {
return Object.values(submodules);
}
+ /**
+ * All types, even those in submodules and nested submodules.
+ */
+ public get types(): readonly Type[] {
+ return Object.values(this.typeMap);
+ }
+
+ /**
+ * Return all types in the current assembly/submodule and all submodules underneath
+ */
+ public get allTypes(): readonly Type[] {
+ return [...this.types, ...this.allSubmodules.flatMap((s) => s.types)];
+ }
+
public findType(fqn: string) {
const type = this.tryFindType(fqn);
if (!type) {
diff --git a/packages/jsii-reflect/package.json b/packages/jsii-reflect/package.json
index 1f465d95eb..577c9f8d51 100644
--- a/packages/jsii-reflect/package.json
+++ b/packages/jsii-reflect/package.json
@@ -44,10 +44,10 @@
"devDependencies": {
"@scope/jsii-calc-lib": "^0.0.0",
"@types/fs-extra": "^9.0.13",
- "@types/jest": "^27.0.2",
- "@types/node": "^12.20.28",
- "eslint": "^7.32.0",
- "jest": "^27.2.4",
+ "@types/jest": "^27.0.3",
+ "@types/node": "^12.20.37",
+ "eslint": "^8.3.0",
+ "jest": "^27.3.1",
"jsii": "^0.0.0",
"jsii-build-tools": "^0.0.0",
"jsii-calc": "^3.20.120",
diff --git a/packages/jsii-reflect/test/independent.test.ts b/packages/jsii-reflect/test/independent.test.ts
index 14f5822f36..dd0f9d546e 100644
--- a/packages/jsii-reflect/test/independent.test.ts
+++ b/packages/jsii-reflect/test/independent.test.ts
@@ -1,10 +1,9 @@
-import { PackageInfo, sourceToAssemblyHelper } from 'jsii';
-
import * as reflect from '../lib';
+import { assemblyFromSource } from './util';
test('get full github source location for a class or method', async () => {
// WHEN
- const assembly = await loadSource(
+ const assembly = await assemblyFromSource(
`
export class Foo {
public bar() {
@@ -27,12 +26,3 @@ test('get full github source location for a class or method', async () => {
'https://github.com/aws/jsii/blob/master/some/sub/dir/index.ts#L1',
);
});
-
-async function loadSource(
- source: string,
- cb: (obj: PackageInfo) => void,
-): Promise {
- const ass = await sourceToAssemblyHelper(source, cb);
- const ts = new reflect.TypeSystem();
- return ts.addAssembly(new reflect.Assembly(ts, ass));
-}
diff --git a/packages/jsii-reflect/test/type-system.test.ts b/packages/jsii-reflect/test/type-system.test.ts
index 57d361a3d2..ef799095ab 100644
--- a/packages/jsii-reflect/test/type-system.test.ts
+++ b/packages/jsii-reflect/test/type-system.test.ts
@@ -3,7 +3,7 @@ import { Stability } from '@jsii/spec';
import * as path from 'path';
import { TypeSystem } from '../lib';
-import { typeSystemFromSource } from './util';
+import { typeSystemFromSource, assemblyFromSource } from './util';
let typesys: TypeSystem;
@@ -411,6 +411,15 @@ test('TypeSystem.methods', async () => {
expect(ts.methods).toHaveLength(2);
});
+test('Assembly allTypes includes submodule types', async () => {
+ const asm = await assemblyFromSource({
+ 'index.ts': 'export * as submod from "./submod";',
+ 'submod.ts': `export class Foo {}`,
+ });
+
+ expect(asm.allTypes.map((t) => t.fqn)).toEqual(['testpkg.submod.Foo']);
+});
+
function resolveModuleDir(name: string) {
return path.dirname(require.resolve(`${name}/package.json`));
}
diff --git a/packages/jsii-reflect/test/util.ts b/packages/jsii-reflect/test/util.ts
index 0ceda68ff4..575213a6a6 100644
--- a/packages/jsii-reflect/test/util.ts
+++ b/packages/jsii-reflect/test/util.ts
@@ -1,10 +1,20 @@
-import { sourceToAssemblyHelper } from 'jsii/lib/helpers';
+import { sourceToAssemblyHelper, MultipleSourceFiles, PackageInfo } from 'jsii';
import { Assembly, TypeSystem } from '../lib';
-export async function typeSystemFromSource(source: string) {
- const assembly = await sourceToAssemblyHelper(source);
+export async function typeSystemFromSource(
+ source: string | MultipleSourceFiles,
+ cb?: (obj: PackageInfo) => void,
+) {
+ const asm = await assemblyFromSource(source, cb);
+ return asm.system;
+}
+
+export async function assemblyFromSource(
+ source: string | MultipleSourceFiles,
+ cb?: (obj: PackageInfo) => void,
+): Promise {
+ const ass = await sourceToAssemblyHelper(source, cb);
const ts = new TypeSystem();
- ts.addAssembly(new Assembly(ts, assembly));
- return ts;
+ return ts.addAssembly(new Assembly(ts, ass));
}
diff --git a/packages/jsii-rosetta/README.md b/packages/jsii-rosetta/README.md
index d05d0973c2..7b1e32c7a8 100644
--- a/packages/jsii-rosetta/README.md
+++ b/packages/jsii-rosetta/README.md
@@ -5,36 +5,52 @@ Utility to transcribe example code snippets from TypeScript to other jsii langua
Has knowledge about jsii language translation conventions to do the translations. Only supports a limited set of
TypeScript language features.
-## Compilability
+## 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 right thing in all instances.
+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:
+
+```ts
+someObject.someMethod('foo', {
+ bar: 3,
+});
+```
-If the samples don't compile or don't have full type information:
+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:
-- No way to declare typed variables for Java and C#.
-- Can only "see" the fields of structs as far as they are declared in the same snippet. Inherited fields or structs
- declared not in the same snippet are invisible.
-- When we explode a struct parameter into keyword parameters and we pass it on to another callable, we can't know which
- keyword arguments the called function actually takes so we just pass all of them (might be too many).
-- When structs contain nested structs, Python and other languages need to know the types of these fields to generate the
- right calls.
-- Object literals are used both to represent structs as well as to represent dictionaries, and without type information
- it's impossible to determine which is which.
+```ts
+import * as myLib from 'some-library';
+
+declare const someObject: myLib.SomeClass;
-### Enforcing Compilability
+someObject.someMethod('foo', {
+ bar: 3,
+});
+```
-Package owners can enable enforcement of compiling code sample by setting the `jsii.rosetta.strict` assembly metadata
-entry to `true`:
+### Enforcing correct examples
+
+By default, Rosetta will accept non-compiling examples. If you set
+`jsii.metadata.jsii.rosetta.strict` to `true` in your `package.json`,
+the Rosetta command will fail if any example contains an error:
```js
/// package.json
{
- // ...
"jsii": {
- // ...
"metadata": {
"jsii": {
"rosetta": {
@@ -42,175 +58,193 @@ entry to `true`:
}
}
}
- // ...
- },
- // ...
+ }
}
```
-This effectively enables the `--strict` option (which is equivalent to setting `--compile` and `--fail`, causing
-`jsii-rosetta` to exit in error if any code sample fails to compile) when translating code samples from the assembly.
+### Fixtures
-## Hiding code from samples
+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:
-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).
+```ts
+const * as module from '@some/dependency';
-This package supports hiding parts of the original source after translation.
+class MyClass {
+ constructor() {
+ const obj = new MyObject();
-To mark special locations in the source tree, we can use one of three mechanisms:
+ /// here
+ }
+}
+```
-- 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 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 `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).
+The default file loaded as a fixture is called `rosetta/default.ts-fixture` in
+the package directory (if it exists).
-A handy mnemonic for this feature is that you can use it to "send your code into the void".
+Examples can request an alternative fixture by specifying a `fixture` parameter
+as part of the code block fence:
-### Hiding statements
+````text
+```ts fixture=some-fixture
+````
-Statement hiding looks like this:
+Or opt out of using the default fixture by specifying `nofixture`:
-```ts
-before(); // will be shown
+````text
+```ts nofixture
+````
-void 0; // start hiding (the argument to 'void' doesn't matter)
-middle(); // will not be shown
-void 'show'; // stop hiding
+To specify fixtures in an `@example` block, use an accompanying `@exampleMetadata` tag:
-after(); // will be shown again
-```
+````text
+/**
+ * My cool class
+ *
+ * @exampleMetadata fixture=with-setup
+ * @example
+ *
+ * new MyCoolClass();
+ */
+````
-### Hiding expressions
+## Rosetta for package publishers
-For hiding expressions, we use `comma` expressions to attach a `void` statement to an expression value without changing
-the meaning of the code.
+This section describes how Rosetta integrates into your build process.
-Example:
-
-```ts
-foo(1, 2, (void 1, 3));
-```
+### Extract
-Will render as
+Rosetta has a number of subcommands. The most important one is `jsii-rosetta extract`.
-```
-foo(1, 2)
-```
+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".
-Also supports a visible ellipsis:
+A couple of things to note here:
-```ts
-const x = (void '...', 3);
-```
-
-Renders to:
-
-```
-x = ...
-```
+* 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 `import`s. 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.
-### Hiding across AST nodes
+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.
-Use special comment directives:
+An example invocation of `jsii-rosetta extract` looks like this:
-```ts
-before();
-/// !hide
-notShown();
-/// !show
-after();
+```sh
+jsii-rosetta extract --directory some/dir $(find . -name .jsii)
```
-(May also start with `/// !show` and `/// !hide`).
+#### Running in parallel
-## Fixtures
+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.
-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 may look like this:
+`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.
-```ts
-const module = require('@some/dependency');
+If you get out of memory errors running too many workers, run a command like
+this to raise the memory allowed for your workers:
-class MyClass {
- constructor() {
- const obj = new MyObject();
-
- /// here
- }
-}
+```sh
+/sbin/sysctl -w vm.max_map_count=2251954
```
-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.
+#### Caching
-The default file loaded as a fixture is called `rosetta/default.ts-fixture` in the package directory (if it exists).
+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.
-Examples can request an alternative fixture by specifying a `fixture` parameter as part of the code block fence:
+To that end, rosetta can reuse translations from a cache, and write
+new translations into the same cache:
- ` ` `ts fixture=some-fixture
- ...
+```sh
+jsii-rosetta extract \
+ --directory some/dir \
+ --cache cache.json \
+ [--trim-cache] \
+ $(find . -name .jsii)
+```
-Or opt out of using the default fixture by specifying `nofixture`.
+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).
-## Build integration
+### Infuse
-Because we want to control the compilation environment when compiling examples, extracting and compiling all samples can
-be run as an external build step in a monorepo. This allows you to set up an environment with all desired packages and
-compile all samples in one go.
+The `jsii-rosetta infuse` command increases the coverage of examples for classes
+in the assembly.
-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"
-(which is a lookup map from the original snippet to all of its translations).
+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.
-A translation tablet will automatically be used by `jsii-pacmak` if present, so it can subsitute the translated examples
-into the converted source code when writing out the converted code. When not given a tablet, `jsii-pacmak` can still
-live-convert samples, but you will not have the fine control over the compilation environment that you would have if you
-were to use the `extract` command.
+Note that in order to do this, `infuse` will *modify* the assemblies it is
+given.
-Works like this:
+`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 --compile $(find . -name .jsii) --directory some/dir
-$ jsii-pacmak --samples-tablet .jsii-samples.tbl
+```sh
+jsii-rosetta extract \
+ --directory some/dir \
+ --infuse \
+ $(find . -name .jsii)
```
-(The `extract` command is the default and may be omitted, but if you're passing assembliess as arguments you should
-terminate the option list by passing `--`).
+### Translations and pacmak
-### Running in parallel
+`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.
-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 if support for NodeJS Worker Threads is detected.
+When a translation for a code sample cannot be found, `pacmak` can be configured
+to do one of the following:
-`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.
+* 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
-If you get out of memory errors running too many workers, run a command like this to up the memory allowed for your workers:
+Example:
+```sh
+jsii-pacmak \
+ [--rosetta-tablet=global.json] \
+ [--rosetta-unknown-snippets=verbatim|translate|fail]
```
-$ /sbin/sysctl -w vm.max_map_count=2251954
-```
-
-## Infuse
-
-The `rosetta infuse` feature will copy code samples (for example, as found in
-the README), to the classes that are referenced in the code sample. This will make
-sure the same examples are rendered in multiple places, and increases their visibility
-without additional work on the author's part.
-
-The infuse command will modify the relevant assemblies and tablet files:
-
-- Assembly: it will add new `example` sections to types in the assembly.
-- Tablet: every newly added `example` will require a new copy of the example (with
- a unique identifier) added to the tablet.
### Data flow
@@ -253,3 +287,82 @@ The diagram below shows how data flows through the jsii tools when used together
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:
+
+```ts
+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:
+
+```ts
+foo(1, 2, (void 1, 3));
+```
+
+Will render as
+
+```ts
+foo(1, 2)
+```
+
+Also supports a visible ellipsis:
+
+```ts
+const x = (void '...', 3);
+```
+
+Renders to:
+
+```ts
+x = ...
+```
+
+#### Hiding across AST nodes
+
+Use special comment directives:
+
+```ts
+before();
+/// !hide
+notShown();
+/// !show
+after();
+```
+
+(May also start with `/// !show` and `/// !hide`).
diff --git a/packages/jsii-rosetta/bin/jsii-rosetta.ts b/packages/jsii-rosetta/bin/jsii-rosetta.ts
index 83ffa41a78..8fbae7a2a2 100644
--- a/packages/jsii-rosetta/bin/jsii-rosetta.ts
+++ b/packages/jsii-rosetta/bin/jsii-rosetta.ts
@@ -4,12 +4,13 @@ import * as fs from 'fs-extra';
import * as path from 'path';
import * as yargs from 'yargs';
-import { TranslateResult, DEFAULT_TABLET_NAME, translateTypeScript, RosettaDiagnostic } from '../lib';
+import { TranslateResult, translateTypeScript, RosettaDiagnostic } from '../lib';
import { translateMarkdown } from '../lib/commands/convert';
-import { extractSnippets } from '../lib/commands/extract';
+import { extractAndInfuse, extractSnippets, ExtractOptions } from '../lib/commands/extract';
import { infuse, DEFAULT_INFUSION_RESULTS_NAME } from '../lib/commands/infuse';
import { readTablet } from '../lib/commands/read';
import { transliterateAssembly } from '../lib/commands/transliterate';
+import { trimCache } from '../lib/commands/trim-cache';
import { TargetLanguage } from '../lib/languages';
import { PythonVisitor } from '../lib/languages/python';
import { VisualizeAstVisitor } from '../lib/languages/visualize';
@@ -69,37 +70,31 @@ function main() {
'(EXPERIMENTAL) mutates one or more assemblies by adding documentation examples to top-level types',
(command) =>
command
- .positional('TABLET', {
- type: 'string',
- required: true,
- describe: 'Language tablet to read',
- })
.positional('ASSEMBLY', {
type: 'string',
string: true,
default: new Array(),
describe: 'Assembly or directory to mutate',
})
- .option('log', {
+ .option('log-file', {
alias: 'l',
- type: 'boolean',
- describe: 'Test all algorithms and log results to an html file',
- default: false,
- })
- .option('output', {
- alias: 'o',
type: 'string',
describe: 'Output file to store logging results. Ignored if -log is not true',
default: DEFAULT_INFUSION_RESULTS_NAME,
})
- .demandOption('TABLET'),
+ .option('cache-to', {
+ alias: 'o',
+ type: 'string',
+ describe: 'Append all translated snippets to the given tablet file',
+ requiresArg: true,
+ default: undefined,
+ }),
wrapHandler(async (args) => {
const absAssemblies = (args.ASSEMBLY.length > 0 ? args.ASSEMBLY : ['.']).map((x) => path.resolve(x));
- const absOutput = path.resolve(args.output);
- const result = await infuse(absAssemblies, args.TABLET, {
- outputFile: absOutput,
- log: args.log,
- tabletOutputFile: args.TABLET,
+ const cacheToFile = fmap(args['cache-to'], path.resolve);
+ const result = await infuse(absAssemblies, {
+ logFile: args['log-file'],
+ cacheToFile: cacheToFile,
});
let totalTypes = 0;
@@ -131,16 +126,16 @@ function main() {
describe: 'Assembly or directory to extract from',
})
.option('output', {
- alias: 'o',
type: 'string',
- describe: 'Output file where to store the sample tablets',
- default: DEFAULT_TABLET_NAME,
+ describe: 'Additional output file where to store translated samples (deprecated, alias for --cache-to)',
+ requiresArg: true,
+ default: undefined,
})
.option('compile', {
alias: 'c',
type: 'boolean',
- describe: 'Try compiling',
- default: false,
+ describe: 'Try compiling (on by default, use --no-compile to switch off)',
+ default: true,
})
.option('directory', {
alias: 'd',
@@ -153,6 +148,11 @@ function main() {
describe: 'Extract only snippets with given ids',
default: new Array(),
})
+ .option('infuse', {
+ type: 'boolean',
+ describe: 'bundle this command with the infuse command',
+ default: false,
+ })
.option('fail', {
alias: 'f',
type: 'boolean',
@@ -168,10 +168,33 @@ function main() {
alias: 'C',
type: 'string',
// eslint-disable-next-line prettier/prettier
- describe: 'Reuse translations from the given tablet file if the snippet and type definitions did not change',
+ describe:
+ 'Reuse translations from the given tablet file if the snippet and type definitions did not change',
+ requiresArg: true,
+ default: undefined,
+ })
+ .option('cache-to', {
+ alias: 'o',
+ type: 'string',
+ describe: 'Append all translated snippets to the given tablet file',
requiresArg: true,
default: undefined,
})
+ .conflicts('cache-to', 'output')
+ .option('cache', {
+ alias: 'k',
+ type: 'string',
+ describe: 'Alias for --cache-from and --cache-to together',
+ requiresArg: true,
+ default: undefined,
+ })
+ .conflicts('cache', 'cache-from')
+ .conflicts('cache', 'cache-to')
+ .option('trim-cache', {
+ alias: 'T',
+ type: 'boolean',
+ describe: 'Remove translations that are not referenced by any of the assemblies anymore from the cache',
+ })
.option('strict', {
alias: 'S',
type: 'boolean',
@@ -192,19 +215,26 @@ function main() {
// 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 absOutput = path.resolve(args.output);
- const absCache = fmap(args['cache-from'], path.resolve);
+
+ const absCacheFrom = fmap(args.cache ?? args['cache-from'], path.resolve);
+ const absCacheTo = fmap(args.cache ?? args['cache-to'] ?? args.output, path.resolve);
+
if (args.directory) {
process.chdir(args.directory);
}
- const result = await extractSnippets(absAssemblies, {
- outputFile: absOutput,
+ const extractOptions: ExtractOptions = {
includeCompilerDiagnostics: !!args.compile,
validateAssemblies: args['validate-assemblies'],
only: args.include,
- cacheTabletFile: absCache,
- });
+ cacheFromFile: absCacheFrom,
+ cacheToFile: absCacheTo,
+ trimCache: args['trim-cache'],
+ };
+
+ const result = args.infuse
+ ? await extractAndInfuse(absAssemblies, extractOptions)
+ : await extractSnippets(absAssemblies, extractOptions);
handleDiagnostics(result.diagnostics, args.fail, result.tablet.count);
}),
@@ -266,6 +296,30 @@ function main() {
return transliterateAssembly(assemblies, languages, args);
}),
)
+ .command(
+ 'trim-cache [ASSEMBLY..]',
+ 'Retain only those snippets in the cache which occur in one of the given assemblies',
+ (command) =>
+ command
+ .positional('TABLET', {
+ type: 'string',
+ required: true,
+ describe: 'Language tablet to trim',
+ })
+ .positional('ASSEMBLY', {
+ type: 'string',
+ string: true,
+ default: new Array(),
+ describe: 'Assembly or directory to search',
+ })
+ .demandOption('TABLET'),
+ wrapHandler(async (args) => {
+ await trimCache({
+ cacheFile: args.TABLET,
+ assemblyLocations: args.ASSEMBLY,
+ });
+ }),
+ )
.command(
'read [KEY] [LANGUAGE]',
'Display snippets in a language tablet file',
diff --git a/packages/jsii-rosetta/lib/commands/extract.ts b/packages/jsii-rosetta/lib/commands/extract.ts
index e2a4f90b1a..b43ac224b8 100644
--- a/packages/jsii-rosetta/lib/commands/extract.ts
+++ b/packages/jsii-rosetta/lib/commands/extract.ts
@@ -1,10 +1,14 @@
-import { loadAssemblies, allTypeScriptSnippets } from '../jsii/assemblies';
+import * as path from 'path';
+
+import { loadAssemblies, allTypeScriptSnippets, loadAllDefaultTablets } from '../jsii/assemblies';
import * as logging from '../logging';
import { RosettaTranslator, RosettaTranslatorOptions } from '../rosetta-translator';
-import { TypeScriptSnippet } from '../snippet';
+import { TypeScriptSnippet, SnippetParameters } from '../snippet';
import { snippetKey } from '../tablets/key';
-import { LanguageTablet } from '../tablets/tablets';
+import { LanguageTablet, DEFAULT_TABLET_NAME } from '../tablets/tablets';
import { RosettaDiagnostic } from '../translate';
+import { groupBy, isDefined } from '../util';
+import { infuse } from './infuse';
export interface ExtractResult {
diagnostics: RosettaDiagnostic[];
@@ -12,15 +16,26 @@ export interface ExtractResult {
}
export interface ExtractOptions {
- readonly outputFile: string;
- readonly includeCompilerDiagnostics: boolean;
- readonly validateAssemblies: boolean;
+ readonly includeCompilerDiagnostics?: boolean;
+ readonly validateAssemblies?: boolean;
readonly only?: string[];
/**
* A tablet file to be loaded and used as a source for caching
*/
- readonly cacheTabletFile?: string;
+ readonly cacheFromFile?: string;
+
+ /**
+ * A tablet file to append translated snippets to
+ */
+ readonly cacheToFile?: string;
+
+ /**
+ * Trim cache to only contain translations found in the current assemblies
+ *
+ * @default false
+ */
+ readonly trimCache?: boolean;
/**
* Make a translator (just for testing)
@@ -28,26 +43,46 @@ export interface ExtractOptions {
readonly translatorFactory?: (opts: RosettaTranslatorOptions) => RosettaTranslator;
}
+export async function extractAndInfuse(
+ assemblyLocations: string[],
+ options: ExtractOptions,
+ loose = false,
+): Promise {
+ const result = await extractSnippets(assemblyLocations, options, loose);
+ await infuse(assemblyLocations, {
+ cacheFromFile: options.cacheFromFile,
+ cacheToFile: options.cacheToFile,
+ });
+ return result;
+}
+
/**
* Extract all samples from the given assemblies into a tablet
*/
export async function extractSnippets(
assemblyLocations: string[],
- options: ExtractOptions,
+ options: ExtractOptions = {},
loose = false,
): Promise {
const only = options.only ?? [];
logging.info(`Loading ${assemblyLocations.length} assemblies`);
- const assemblies = await loadAssemblies(assemblyLocations, options.validateAssemblies);
+ const assemblies = await loadAssemblies(assemblyLocations, options.validateAssemblies ?? false);
let snippets = Array.from(allTypeScriptSnippets(assemblies, loose));
if (only.length > 0) {
snippets = filterSnippets(snippets, only);
}
+ // Map every assembly to a list of snippets, so that we know what implicit
+ // tablet to write the translations to later on.
+ const snippetsPerAssembly = groupBy(
+ snippets.map((s) => ({ key: snippetKey(s), location: projectDirectory(s) })),
+ (x) => x.location,
+ );
+
const translatorOptions: RosettaTranslatorOptions = {
- includeCompilerDiagnostics: options.includeCompilerDiagnostics,
+ includeCompilerDiagnostics: options.includeCompilerDiagnostics ?? false,
assemblies: assemblies.map((a) => a.assembly),
};
@@ -55,10 +90,17 @@ export async function extractSnippets(
? options.translatorFactory(translatorOptions)
: new RosettaTranslator(translatorOptions);
- if (options.cacheTabletFile) {
- await translator.loadCache(options.cacheTabletFile);
+ // Prime the snippet cache with:
+ // - Cache source file
+ // - Default tablets found next to each assembly
+ if (options.cacheFromFile) {
+ await translator.addToCache(options.cacheFromFile);
+ }
+ translator.addTabletsToCache(...Object.values(await loadAllDefaultTablets(assemblies)));
+
+ if (translator.hasCache()) {
const { translations, remaining } = translator.readFromCache(snippets);
- logging.info(`Reused ${translations.length} translations from cache ${options.cacheTabletFile}`);
+ logging.info(`Reused ${translations.length} translations from cache`);
snippets = remaining;
}
@@ -80,8 +122,27 @@ export async function extractSnippets(
logging.info('Nothing left to translate');
}
- logging.info(`Saving language tablet to ${options.outputFile}`);
- await translator.tablet.save(options.outputFile);
+ // Save to individual tablet files, and optionally append to the output file
+ await Promise.all(
+ Object.entries(snippetsPerAssembly).map(async ([location, snips]) => {
+ const asmTabletFile = path.join(location, DEFAULT_TABLET_NAME);
+ logging.debug(`Writing ${snips.length} translations to ${asmTabletFile}`);
+ const translations = snips.map(({ key }) => translator.tablet.tryGetSnippet(key)).filter(isDefined);
+
+ const asmTablet = new LanguageTablet();
+ asmTablet.addSnippets(...translations);
+ await asmTablet.save(asmTabletFile);
+ }),
+ );
+
+ if (options.cacheToFile) {
+ logging.info(`Adding translations to ${options.cacheToFile}`);
+ const output = options.trimCache
+ ? new LanguageTablet()
+ : await LanguageTablet.fromOptionalFile(options.cacheToFile);
+ output.addTablet(translator.tablet);
+ await output.save(options.cacheToFile);
+ }
return { diagnostics, tablet: translator.tablet };
}
@@ -92,3 +153,11 @@ export async function extractSnippets(
function filterSnippets(ts: TypeScriptSnippet[], includeIds: string[]) {
return ts.filter((t) => includeIds.includes(snippetKey(t)));
}
+
+function projectDirectory(ts: TypeScriptSnippet) {
+ const dir = ts.parameters?.[SnippetParameters.$PROJECT_DIRECTORY];
+ if (!dir) {
+ throw new Error(`Snippet does not have associated project directory: ${JSON.stringify(ts.location)}`);
+ }
+ return dir;
+}
diff --git a/packages/jsii-rosetta/lib/commands/infuse.ts b/packages/jsii-rosetta/lib/commands/infuse.ts
index 7962c79fc1..6d20cf5e95 100644
--- a/packages/jsii-rosetta/lib/commands/infuse.ts
+++ b/packages/jsii-rosetta/lib/commands/infuse.ts
@@ -1,9 +1,19 @@
import * as spec from '@jsii/spec';
import * as fs from 'fs-extra';
+import * as path from 'path';
-import { loadAssemblies, replaceAssembly } from '../jsii/assemblies';
+import {
+ loadAssemblies,
+ replaceAssembly,
+ loadAllDefaultTablets,
+ LoadedAssembly,
+ allTypeScriptSnippets,
+} from '../jsii/assemblies';
+import { renderMetadataline, TypeScriptSnippet } from '../snippet';
import { SnippetSelector, mean, meanLength, shortest, longest } from '../snippet-selectors';
-import { LanguageTablet, TranslatedSnippet } from '../tablets/tablets';
+import { snippetKey } from '../tablets/key';
+import { LanguageTablet, TranslatedSnippet, DEFAULT_TABLET_NAME } from '../tablets/tablets';
+import { isDefined, mkDict, fmap, indexBy } from '../util';
export interface InfuseResult {
readonly coverageResults: Record;
@@ -15,14 +25,17 @@ export interface InfuseTypes {
}
export interface InfuseOptions {
- readonly outputFile?: string;
+ readonly logFile?: string;
- readonly log?: boolean;
+ /**
+ * Where to read additional translations
+ */
+ readonly cacheFromFile?: string;
/**
- * Where to write the updated tablet back
+ * In addition to the implicit tablets, also write all added examples to this additional output tablet
*/
- readonly tabletOutputFile?: string;
+ readonly cacheToFile?: string;
}
export const DEFAULT_INFUSION_RESULTS_NAME = 'infusion-results.html';
@@ -40,68 +53,84 @@ class DefaultRecord {
}
}
-export async function infuse(
- assemblyLocations: string[],
- tabletFile: string,
- options?: InfuseOptions,
-): Promise {
+/**
+ * Infuse will analyze the snippets in a set of tablets, and update the assembly to add
+ * examples to types that don't have any yet, based on snippets that use the given type.
+ */
+export async function infuse(assemblyLocations: string[], options?: InfuseOptions): Promise {
let stream: fs.WriteStream | undefined = undefined;
- if (options?.log) {
- if (!options.outputFile) {
- throw new Error("If 'log' is set, 'outputFile' must be set as well.");
- }
-
+ if (options?.logFile) {
// Create stream for html file and insert some styling
- stream = fs.createWriteStream(options.outputFile, {
- encoding: 'utf-8',
- });
+ stream = fs.createWriteStream(options.logFile, { encoding: 'utf-8' });
startFile(stream);
}
// Load tablet file and assemblies
- const tab = new LanguageTablet();
- await tab.load(tabletFile);
- const assemblies = await loadAssemblies(assemblyLocations, true);
-
- const snippetsFromFqn = mapFqns(tab);
- const coverageResults: Record = {};
- for (const { assembly, directory } of assemblies) {
- stream?.write(`@aws-cdk/${directory.split('/').pop()}
\n`);
-
- let typesWithInsertedExamples = 0;
- const filteredTypes = filterForTypesWithoutExamples(assembly.types ?? {});
- for (const [typeFqn, type] of Object.entries(filteredTypes)) {
- if (snippetsFromFqn[typeFqn] !== undefined) {
- const meanResult = mean(snippetsFromFqn[typeFqn]);
- if (options?.log) {
- const selected = Object.entries(ADDITIONAL_SELECTORS).map(
- ([name, fn]) => [name, fn(snippetsFromFqn[typeFqn])] as const,
- );
- const selectedFromSelector = {
- ...makeDict(selected),
- mean: meanResult,
- };
- logOutput(stream, typeFqn, createHtmlEntry(selectedFromSelector));
- }
- insertExample(meanResult, type, tab);
- typesWithInsertedExamples++;
- }
- }
+ const assemblies = await loadAssemblies(assemblyLocations, false);
+ const defaultTablets = await loadAllDefaultTablets(assemblies);
- // eslint-disable-next-line no-await-in-loop
- await replaceAssembly(assembly, directory);
- coverageResults[directory] = {
- types: Object.keys(filteredTypes).length,
- typesWithInsertedExamples,
- };
+ const availableTranslations = new LanguageTablet();
+ if (options?.cacheFromFile) {
+ availableTranslations.addTablet(await LanguageTablet.fromOptionalFile(options.cacheFromFile));
}
+ availableTranslations.addTablets(...Object.values(defaultTablets));
+
+ const { translationsByFqn, originalsByKey } = availableSnippetsPerFqn(assemblies, availableTranslations);
+
+ const additionalOutputTablet = options?.cacheToFile
+ ? await LanguageTablet.fromOptionalFile(options?.cacheToFile)
+ : new LanguageTablet();
+
+ const coverageResults = mkDict(
+ await Promise.all(
+ assemblies.map(async ({ assembly, directory }) => {
+ stream?.write(`${assembly.name}
\n`);
+
+ const implicitTablet = defaultTablets[directory];
+ if (!implicitTablet) {
+ throw new Error(`No tablet found for ${directory}`);
+ }
+
+ let insertedExamples = 0;
+ const filteredTypes = filterForTypesWithoutExamples(assembly.types ?? {});
+ for (const [typeFqn, type] of Object.entries(filteredTypes)) {
+ const available = translationsByFqn[typeFqn];
+ if (!available) {
+ continue;
+ }
+
+ const example = pickBestExample(typeFqn, available, stream);
+ const original = originalsByKey[example.key];
+ insertExample(example, original, type, [implicitTablet, additionalOutputTablet]);
+ insertedExamples++;
+ }
+
+ if (insertedExamples > 0) {
+ // Save the updated assembly and implicit tablets
+ // eslint-disable-next-line no-await-in-loop
+ await Promise.all([
+ replaceAssembly(assembly, directory),
+ implicitTablet.save(path.join(directory, DEFAULT_TABLET_NAME)),
+ ]);
+ }
+
+ return [
+ directory,
+ {
+ types: Object.keys(filteredTypes).length,
+ typesWithInsertedExamples: insertedExamples,
+ } as InfuseTypes,
+ ] as const;
+ }),
+ ),
+ );
stream?.close();
// If we copied examples onto different types, we'll also have inserted new snippets
// with different keys into the tablet. We must now write the updated tablet somewhere.
- if (options?.tabletOutputFile) {
- await tab.save(options.tabletOutputFile);
+ if (options?.cacheToFile) {
+ await additionalOutputTablet.save(options.cacheToFile);
}
return {
@@ -109,6 +138,19 @@ export async function infuse(
};
}
+function pickBestExample(typeFqn: string, choices: TranslatedSnippet[], logStream?: fs.WriteStream) {
+ const meanResult = mean(choices);
+ if (logStream) {
+ const selected = Object.entries(ADDITIONAL_SELECTORS).map(([name, fn]) => [name, fn(choices)] as const);
+ const selectedFromSelector = {
+ ...makeDict(selected),
+ mean: meanResult,
+ };
+ logOutput(logStream, typeFqn, createHtmlEntry(selectedFromSelector));
+ }
+ return meanResult;
+}
+
function startFile(stream: fs.WriteStream) {
stream.write('