Skip to content

Commit 359e74f

Browse files
authored
feat(rosetta): tablets can be compressed (#3652)
Usage: `yarn rosetta extract --compress-tablet` This should reduce the `.jsii.tabl.json` size by about 90%. --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
1 parent 8d84440 commit 359e74f

File tree

4 files changed

+66
-9
lines changed

4 files changed

+66
-9
lines changed

packages/jsii-rosetta/bin/jsii-rosetta.ts

+7
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,12 @@ function main() {
228228
describe: 'Ignore missing fixtures and literate markdown files instead of failing',
229229
type: 'boolean',
230230
})
231+
.options('compress-tablet', {
232+
alias: 'z',
233+
type: 'boolean',
234+
describe: 'Compress the resulting tablet file',
235+
default: false,
236+
})
231237
.conflicts('loose', 'strict')
232238
.conflicts('loose', 'fail'),
233239
wrapHandler(async (args) => {
@@ -252,6 +258,7 @@ function main() {
252258
cacheToFile: absCacheTo,
253259
trimCache: args['trim-cache'],
254260
loose: args.loose,
261+
compressTablet: args['compress-tablet'],
255262
};
256263

257264
const result = args.infuse

packages/jsii-rosetta/lib/commands/extract.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as logging from '../logging';
55
import { RosettaTranslator, RosettaTranslatorOptions } from '../rosetta-translator';
66
import { TypeScriptSnippet, SnippetParameters } from '../snippet';
77
import { snippetKey } from '../tablets/key';
8-
import { LanguageTablet, DEFAULT_TABLET_NAME } from '../tablets/tablets';
8+
import { LanguageTablet, DEFAULT_TABLET_NAME, DEFAULT_TABLET_NAME_COMPRESSED } from '../tablets/tablets';
99
import { RosettaDiagnostic } from '../translate';
1010
import { groupBy, isDefined } from '../util';
1111
import { infuse } from './infuse';
@@ -73,6 +73,13 @@ export interface ExtractOptions {
7373
* @default false
7474
*/
7575
readonly allowDirtyTranslations?: boolean;
76+
77+
/**
78+
* Compress the resulting tablet file
79+
*
80+
* @default false
81+
*/
82+
readonly compressTablet?: boolean;
7683
}
7784

7885
export async function extractAndInfuse(assemblyLocations: string[], options: ExtractOptions): Promise<ExtractResult> {
@@ -154,13 +161,16 @@ export async function extractSnippets(
154161
if (options.writeToImplicitTablets ?? true) {
155162
await Promise.all(
156163
Object.entries(snippetsPerAssembly).map(async ([location, snips]) => {
157-
const asmTabletFile = path.join(location, DEFAULT_TABLET_NAME);
164+
const asmTabletFile = path.join(
165+
location,
166+
options.compressTablet ? DEFAULT_TABLET_NAME_COMPRESSED : DEFAULT_TABLET_NAME,
167+
);
158168
logging.debug(`Writing ${snips.length} translations to ${asmTabletFile}`);
159169
const translations = snips.map(({ key }) => translator.tablet.tryGetSnippet(key)).filter(isDefined);
160170

161171
const asmTablet = new LanguageTablet();
162172
asmTablet.addSnippets(...translations);
163-
await asmTablet.save(asmTabletFile);
173+
await asmTablet.save(asmTabletFile, options.compressTablet);
164174
}),
165175
);
166176
}

packages/jsii-rosetta/lib/tablets/tablets.ts

+33-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as fs from 'fs-extra';
22
import * as path from 'path';
3+
import * as zlib from 'zlib';
34

45
import { TargetLanguage } from '../languages';
56
import * as logging from '../logging';
@@ -11,8 +12,16 @@ import { TabletSchema, TranslatedSnippetSchema, ORIGINAL_SNIPPET_KEY } from './s
1112
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires
1213
const TOOL_VERSION = require('../../package.json').version;
1314

15+
/**
16+
* The default name of the tablet file
17+
*/
1418
export const DEFAULT_TABLET_NAME = '.jsii.tabl.json';
1519

20+
/**
21+
* The default name of the compressed tablet file
22+
*/
23+
export const DEFAULT_TABLET_NAME_COMPRESSED = '.jsii.tabl.json.gz';
24+
1625
export const CURRENT_SCHEMA_VERSION = '2';
1726

1827
/**
@@ -121,8 +130,19 @@ export class LanguageTablet {
121130
return this.snippets[snippetKey(typeScriptSource)];
122131
}
123132

133+
/**
134+
* Load the tablet from a file. Will automatically detect if the file is
135+
* compressed and decompress accordingly.
136+
*/
124137
public async load(filename: string) {
125-
const obj = (await fs.readJson(filename, { encoding: 'utf-8' })) as TabletSchema;
138+
let data = await fs.readFile(filename);
139+
// Gzip objects start with 1f 8b 08
140+
if (data[0] === 0x1f && data[1] === 0x8b && data[2] === 0x08) {
141+
// This is a gz object, so we decompress it now...
142+
data = zlib.gunzipSync(data);
143+
}
144+
145+
const obj: TabletSchema = JSON.parse(data.toString('utf-8'));
126146

127147
if (!obj.toolVersion || !obj.snippets) {
128148
throw new Error(`File '${filename}' does not seem to be a Tablet file`);
@@ -147,12 +167,19 @@ export class LanguageTablet {
147167
return Object.values(this.snippets);
148168
}
149169

150-
public async save(filename: string) {
170+
/**
171+
* Saves the tablet schema to a file. If the compress option is passed, then
172+
* the schema will be gzipped before writing to the file.
173+
*/
174+
public async save(filename: string, compress = false) {
151175
await fs.mkdirp(path.dirname(filename));
152-
await fs.writeJson(filename, this.toSchema(), {
153-
encoding: 'utf-8',
154-
spaces: 2,
155-
});
176+
177+
let schema = Buffer.from(JSON.stringify(this.toSchema(), null, 2));
178+
if (compress) {
179+
schema = zlib.gzipSync(schema);
180+
}
181+
182+
await fs.writeFile(filename, schema);
156183
}
157184

158185
private toSchema(): TabletSchema {

packages/jsii-rosetta/test/commands/extract.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ test('extract samples from test assembly', async () => {
7373
expect(tablet.snippetKeys.length).toEqual(1);
7474
});
7575

76+
test('extract can save/load compressed tablets', async () => {
77+
const compressedCacheFile = path.join(assembly.moduleDirectory, 'test.tabl.gz');
78+
await extract.extractSnippets([assembly.moduleDirectory], {
79+
cacheToFile: compressedCacheFile,
80+
...defaultExtractOptions,
81+
});
82+
83+
const tablet = new LanguageTablet();
84+
await tablet.load(compressedCacheFile);
85+
86+
expect(tablet.snippetKeys.length).toEqual(1);
87+
});
88+
7689
test('extract works from compressed test assembly', async () => {
7790
const compressedAssembly = TestJsiiModule.fromSource(
7891
{

0 commit comments

Comments
 (0)