Skip to content

Commit 623c0c1

Browse files
authored
feat: use new read/load assembly functions everywhere (#3600)
This PR utilizes the functions added in #3570 everywhere in the jsii monorepo. The benefit of this is two-fold -- it refactors all places where we load/read assemblies and points them to the source of truth (introduced in #3570). This also means the logic for compressing assemblies will be available for all packages in the jsii monorepo if/when we flip that switch. --- 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 635404a commit 623c0c1

File tree

27 files changed

+425
-133
lines changed

27 files changed

+425
-133
lines changed

packages/@jsii/kernel/src/kernel.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as spec from '@jsii/spec';
2+
import { loadAssemblyFromPath } from '@jsii/spec';
23
import * as cp from 'child_process';
34
import * as fs from 'fs-extra';
45
import * as os from 'os';
@@ -114,13 +115,12 @@ export class Kernel {
114115
}
115116

116117
// read .jsii metadata from the root of the package
117-
const jsiiMetadataFile = path.join(packageDir, spec.SPEC_FILE_NAME);
118-
if (!fs.pathExistsSync(jsiiMetadataFile)) {
119-
throw new Error(
120-
`Package tarball ${req.tarball} must have a file named ${spec.SPEC_FILE_NAME} at the root`,
121-
);
118+
let assmSpec;
119+
try {
120+
assmSpec = loadAssemblyFromPath(packageDir);
121+
} catch (e: any) {
122+
throw new Error(`Error for package tarball ${req.tarball}: ${e.message}`);
122123
}
123-
const assmSpec = fs.readJsonSync(jsiiMetadataFile) as spec.Assembly;
124124

125125
// load the module and capture it's closure
126126
const closure = this._execute(

packages/@jsii/runtime/test/kernel-host.test.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { api } from '@jsii/kernel';
22
import * as spec from '@jsii/spec';
3+
import { loadAssemblyFromPath } from '@jsii/spec';
34
import * as child from 'child_process';
45
import * as fs from 'fs';
56
import * as path from 'path';
@@ -86,12 +87,9 @@ function loadRequest(library: string): api.LoadRequest {
8687
};
8788

8889
function loadAssembly(): spec.Assembly {
89-
const assemblyFile = path.resolve(
90-
require.resolve(`${library}/package.json`),
91-
'..',
92-
'.jsii',
90+
return loadAssemblyFromPath(
91+
path.resolve(require.resolve(`${library}/package.json`), '..'),
9392
);
94-
return JSON.parse(fs.readFileSync(assemblyFile, { encoding: 'utf-8' }));
9593
}
9694

9795
function packageLibrary(target: string): void {

packages/@scope/jsii-calc-base-of-base/.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
*.tgz
55

66

7-
# Include .jsii
7+
# Include .jsii and .jsii.gz
88
!.jsii
9+
!.jsii.gz
910

1011

1112
# Exclude jsii outdir

packages/@scope/jsii-calc-base/.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
*.tgz
55

66

7-
# Include .jsii
7+
# Include .jsii and .jsii.gz
88
!.jsii
9+
!.jsii.gz
910

1011

1112
# Exclude jsii outdir

packages/@scope/jsii-calc-lib/.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
*.tgz
55

66

7-
# Include .jsii
7+
# Include .jsii and .jsii.gz
88
!.jsii
9+
!.jsii.gz
910

1011

1112
# Exclude jsii outdir

packages/jsii-calc/.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
!*.d.ts
44
*.tgz
55

6-
# Include .jsii
6+
# Include .jsii and .jsii.gz
77
!.jsii
8+
!.jsii.gz
89

910

1011
# Exclude jsii outdir

packages/jsii-diff/bin/jsii-diff.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ type LoadAssemblyResult = { requested: string; resolved: string } & (
215215
async function loadPackageNameFromAssembly(
216216
options: LoadOptions,
217217
): Promise<string> {
218-
const JSII_ASSEMBLY_FILE = '.jsii';
218+
const JSII_ASSEMBLY_FILE = spec.SPEC_FILE_NAME;
219219
if (!(await fs.pathExists(JSII_ASSEMBLY_FILE))) {
220220
throw new Error(
221221
`No NPM package name given and no ${JSII_ASSEMBLY_FILE} file in the current directory. Please specify a package name.`,

packages/jsii-pacmak/lib/npm-modules.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ async function updateNpmIgnore(
150150
);
151151
}
152152

153-
includePattern('Include .jsii', spec.SPEC_FILE_NAME);
153+
includePattern(
154+
'Include .jsii and .jsii.gz',
155+
spec.SPEC_FILE_NAME,
156+
spec.SPEC_FILE_NAME_COMPRESSED,
157+
);
154158

155159
if (modified) {
156160
await fs.writeFile(npmIgnorePath, `${lines.join('\n')}\n`);

packages/jsii-reflect/lib/type-system.ts

+7-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as jsii from '@jsii/spec';
1+
import { getAssemblyFile, loadAssemblyFromFile } from '@jsii/spec';
22
import * as fs from 'fs-extra';
33
import * as path from 'path';
44

@@ -109,10 +109,7 @@ export class TypeSystem {
109109
// Load the assembly, but don't recurse if we already have an assembly with the same name.
110110
// Validation is not an insignificant time sink, and loading IS insignificant, so do a
111111
// load without validation first. This saves about 2/3rds of processing time.
112-
const asm = await this.loadAssembly(
113-
path.join(moduleDirectory, '.jsii'),
114-
false,
115-
);
112+
const asm = this.loadAssembly(getAssemblyFile(moduleDirectory), false);
116113
if (this.includesAssembly(asm.name)) {
117114
const existing = this.findAssembly(asm.name);
118115
if (existing.version !== asm.version) {
@@ -154,11 +151,11 @@ export class TypeSystem {
154151
}
155152
}
156153

157-
public async loadFile(
154+
public loadFile(
158155
file: string,
159156
options: { isRoot?: boolean; validate?: boolean } = {},
160157
) {
161-
const assembly = await this.loadAssembly(file, options.validate !== false);
158+
const assembly = this.loadAssembly(file, options.validate !== false);
162159
return this.addAssembly(assembly, options);
163160
}
164161

@@ -308,12 +305,9 @@ export class TypeSystem {
308305
* @param file Assembly file to load
309306
* @param validate Whether to validate the assembly or just assume it matches the schema
310307
*/
311-
private async loadAssembly(file: string, validate = true) {
312-
const spec = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
313-
const ass = validate
314-
? jsii.validateAssembly(spec)
315-
: (spec as jsii.Assembly);
316-
return new Assembly(this, ass);
308+
private loadAssembly(file: string, validate = true) {
309+
const contents = loadAssemblyFromFile(file, validate);
310+
return new Assembly(this, contents);
317311
}
318312

319313
private addRoot(asm: Assembly) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { formatLocation } from '../snippet';
55

66
export async function checkCoverage(assemblyLocations: readonly string[]): Promise<void> {
77
logging.info(`Loading ${assemblyLocations.length} assemblies`);
8-
const assemblies = await loadAssemblies(assemblyLocations, false);
8+
const assemblies = loadAssemblies(assemblyLocations, false);
99

1010
const snippets = Array.from(await allTypeScriptSnippets(assemblies, true));
1111

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export async function extractSnippets(
9494
const only = options.only ?? [];
9595

9696
logging.info(`Loading ${assemblyLocations.length} assemblies`);
97-
const assemblies = await loadAssemblies(assemblyLocations, options.validateAssemblies ?? false);
97+
const assemblies = loadAssemblies(assemblyLocations, options.validateAssemblies ?? false);
9898

9999
let snippets = Array.from(await allTypeScriptSnippets(assemblies, options.loose));
100100
if (only.length > 0) {
@@ -170,7 +170,7 @@ export async function extractSnippets(
170170
const output = options.trimCache
171171
? new LanguageTablet()
172172
: await LanguageTablet.fromOptionalFile(options.cacheToFile);
173-
output.addTablet(translator.tablet);
173+
output.addTablets(translator.tablet);
174174
await output.save(options.cacheToFile);
175175
}
176176

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export async function infuse(assemblyLocations: string[], options?: InfuseOption
6666
}
6767

6868
// Load tablet file and assemblies
69-
const assemblies = await loadAssemblies(assemblyLocations, false);
69+
const assemblies = loadAssemblies(assemblyLocations, false);
7070
const defaultTablets = await loadAllDefaultTablets(assemblies);
7171

7272
const availableTranslations = new LanguageTablet();

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

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Assembly, Docs, SPEC_FILE_NAME, Type, TypeKind } from '@jsii/spec';
2-
import { readJson, writeJson } from 'fs-extra';
1+
import { Assembly, Docs, SPEC_FILE_NAME, Type, TypeKind, loadAssemblyFromPath } from '@jsii/spec';
2+
import { writeJson } from 'fs-extra';
33
import { resolve } from 'path';
44

55
import { TargetLanguage } from '../languages';
@@ -98,8 +98,7 @@ export async function transliterateAssembly(
9898
for (const [location, loadAssembly] of assemblies.entries()) {
9999
for (const language of targetLanguages) {
100100
const now = new Date().getTime();
101-
// eslint-disable-next-line no-await-in-loop
102-
const result = await loadAssembly();
101+
const result = loadAssembly();
103102

104103
if (result.targets?.[targetName(language)] == null) {
105104
// This language is not supported by the assembly, so we skip it...
@@ -149,16 +148,16 @@ async function loadAssemblies(
149148
const result = new Map<string, AssemblyLoader>();
150149

151150
for (const directory of directories) {
152-
const loader = () => readJson(resolve(directory, SPEC_FILE_NAME));
151+
const loader = () => loadAssemblyFromPath(directory);
153152
// eslint-disable-next-line no-await-in-loop
154-
await rosetta.addAssembly(await loader(), directory);
153+
await rosetta.addAssembly(loader(), directory);
155154
result.set(directory, loader);
156155
}
157156

158157
return result;
159158
}
160159

161-
type AssemblyLoader = () => Promise<Mutable<Assembly>>;
160+
type AssemblyLoader = () => Mutable<Assembly>;
162161

163162
function transliterateType(type: Type, rosetta: RosettaTabletReader, language: TargetLanguage): void {
164163
transliterateDocs({ api: 'type', fqn: type.fqn }, type.docs);

packages/jsii-rosetta/lib/commands/trim-cache.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface TrimCacheOptions {
1818

1919
export async function trimCache(options: TrimCacheOptions): Promise<void> {
2020
logging.info(`Loading ${options.assemblyLocations.length} assemblies`);
21-
const assemblies = await loadAssemblies(options.assemblyLocations, false);
21+
const assemblies = loadAssemblies(options.assemblyLocations, false);
2222

2323
const snippets = Array.from(await allTypeScriptSnippets(assemblies));
2424

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

+30-37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as spec from '@jsii/spec';
2+
import { loadAssemblyFromFile, loadAssemblyFromPath, getAssemblyFile, writeAssembly } from '@jsii/spec';
23
import * as crypto from 'crypto';
34
import * as fs from 'fs-extra';
45
import * as path from 'path';
@@ -55,35 +56,28 @@ export interface LoadedAssembly {
5556
/**
5657
* Load assemblies by filename or directory
5758
*/
58-
export async function loadAssemblies(
59+
export function loadAssemblies(
5960
assemblyLocations: readonly string[],
6061
validateAssemblies: boolean,
61-
): Promise<readonly LoadedAssembly[]> {
62-
return Promise.all(assemblyLocations.map(loadAssembly));
62+
): readonly LoadedAssembly[] {
63+
return assemblyLocations.map(loadAssembly);
6364

64-
async function loadAssembly(location: string): Promise<LoadedAssembly> {
65-
const stat = await fs.stat(location);
65+
function loadAssembly(location: string): LoadedAssembly {
66+
const stat = fs.statSync(location);
6667
if (stat.isDirectory()) {
67-
return loadAssembly(path.join(location, '.jsii'));
68+
return loadAssembly(getAssemblyFile(location));
6869
}
6970

7071
const directory = path.dirname(location);
7172
const pjLocation = path.join(directory, 'package.json');
7273

73-
const [assembly, packageJson] = await Promise.all([
74-
loadAssemblyFromFile(location, validateAssemblies),
75-
(await fs.pathExists(pjLocation)) ? fs.readJSON(pjLocation, { encoding: 'utf-8' }) : Promise.resolve(undefined),
76-
]);
74+
const assembly = loadAssemblyFromFile(location, validateAssemblies);
75+
const packageJson = fs.pathExistsSync(pjLocation) ? fs.readJSONSync(pjLocation, { encoding: 'utf-8' }) : undefined;
7776

7877
return { assembly, directory, packageJson };
7978
}
8079
}
8180

82-
async function loadAssemblyFromFile(filename: string, validate: boolean): Promise<spec.Assembly> {
83-
const contents = await fs.readJSON(filename, { encoding: 'utf-8' });
84-
return validate ? spec.validateAssembly(contents) : (contents as spec.Assembly);
85-
}
86-
8781
/**
8882
* Load the default tablets for every assembly, if available
8983
*
@@ -236,12 +230,12 @@ export async function allTypeScriptSnippets(
236230
* Replaces the file where the original assembly file *should* be found with a new assembly file.
237231
* Recalculates the fingerprint of the assembly to avoid tampering detection.
238232
*/
239-
export async function replaceAssembly(assembly: spec.Assembly, directory: string): Promise<void> {
240-
const fileName = path.join(directory, '.jsii');
241-
await fs.writeJson(fileName, _fingerprint(assembly), {
242-
encoding: 'utf8',
243-
spaces: 2,
244-
});
233+
export function replaceAssembly(
234+
assembly: spec.Assembly,
235+
directory: string,
236+
{ compress = false }: { compress?: boolean } = {},
237+
) {
238+
writeAssembly(directory, _fingerprint(assembly), { compress });
245239
}
246240

247241
/**
@@ -296,24 +290,23 @@ export function findTypeLookupAssembly(startingDirectory: string): TypeLookupAss
296290
}
297291

298292
function loadLookupAssembly(directory: string): TypeLookupAssembly | undefined {
299-
const assemblyFile = path.join(directory, '.jsii');
300-
if (!fs.pathExistsSync(assemblyFile)) {
293+
try {
294+
const packageJson = fs.readJSONSync(path.join(directory, 'package.json'), { encoding: 'utf-8' });
295+
const assembly: spec.Assembly = loadAssemblyFromPath(directory);
296+
const symbolIdMap = mkDict([
297+
...Object.values(assembly.types ?? {}).map((type) => [type.symbolId ?? '', type.fqn] as const),
298+
...Object.entries(assembly.submodules ?? {}).map(([fqn, mod]) => [mod.symbolId ?? '', fqn] as const),
299+
]);
300+
301+
return {
302+
packageJson,
303+
assembly,
304+
directory,
305+
symbolIdMap,
306+
};
307+
} catch {
301308
return undefined;
302309
}
303-
304-
const packageJson = fs.readJSONSync(path.join(directory, 'package.json'), { encoding: 'utf-8' });
305-
const assembly: spec.Assembly = fs.readJSONSync(assemblyFile, { encoding: 'utf-8' });
306-
const symbolIdMap = mkDict([
307-
...Object.values(assembly.types ?? {}).map((type) => [type.symbolId ?? '', type.fqn] as const),
308-
...Object.entries(assembly.submodules ?? {}).map(([fqn, mod]) => [mod.symbolId ?? '', fqn] as const),
309-
]);
310-
311-
return {
312-
packageJson,
313-
assembly,
314-
directory,
315-
symbolIdMap,
316-
};
317310
}
318311

319312
function findPackageJsonLocation(currentPath: string): string | undefined {

0 commit comments

Comments
 (0)