Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions src/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { normalizeConfigPath } from './helpers';
import { JsiiDiagnostic } from './jsii-diagnostic';
import * as literate from './literate';
import * as bindings from './node-bindings';
import { ProjectInfo } from './project-info';
import { AssemblyTargets, ProjectInfo } from './project-info';
import { isReservedName } from './reserved-words';
import { Sets } from './sets';
import { DeprecatedRemover } from './transforms/deprecated-remover';
Expand Down Expand Up @@ -181,6 +181,8 @@ export class Assembler implements Emitter {

this.validateTypesAgainstPositions();

this.validateSubmoduleConfigs();

// Skip emitting if any diagnostic message is an error
if (this._diagnostics.find((diag) => diag.category === ts.DiagnosticCategory.Error) != null) {
LOG.debug('Skipping emit due to errors.');
Expand Down Expand Up @@ -705,8 +707,17 @@ export class Assembler implements Emitter {
}

function loadSubmoduleTargetConfig(submoduleMain: string): SubmoduleSpec['targets'] {
const jsiirc = path.resolve(submoduleMain, '..', '.jsiirc.json');
if (!fs.existsSync(jsiirc)) {
const dirname = path.dirname(submoduleMain);
const basenameWithoutExtension = path.basename(submoduleMain).replace(/\.ts$/, '');

let jsiirc;
if (basenameWithoutExtension === 'index') {
jsiirc = path.resolve(submoduleMain, '..', '.jsiirc.json');
} else {
jsiirc = path.resolve(dirname, `.${basenameWithoutExtension}.jsiirc.json`);
}

if (!jsiirc || !fs.existsSync(jsiirc)) {
return undefined;
}
const data = JSON.parse(fs.readFileSync(jsiirc, 'utf-8'));
Expand Down Expand Up @@ -2749,6 +2760,56 @@ export class Assembler implements Emitter {
});
}
}

/**
* Make sure that no 2 submodules are emitting into the same target namespaces
*/
private validateSubmoduleConfigs() {
const self = this;
const dotNetnamespaces: Record<string, string[]> = {};
const javaPackages: Record<string, string[]> = {};
const pythonModules: Record<string, string[]> = {};
const goPackages: Record<string, string[]> = {};

for (const submodule of this._submodules.values()) {
const targets = submodule.targets as AssemblyTargets | undefined;

if (targets?.dotnet?.namespace) {
accumList(dotNetnamespaces, targets.dotnet.namespace, submodule.fqn);
}
if (targets?.java?.package) {
accumList(javaPackages, targets.java.package, submodule.fqn);
}
if (targets?.python?.module) {
accumList(pythonModules, targets.python.module, submodule.fqn);
}
if (targets?.go?.packageName) {
accumList(goPackages, targets.go.packageName, submodule.fqn);
}
}

maybeError('dotnet', dotNetnamespaces);
maybeError('java', javaPackages);
maybeError('python', pythonModules);
maybeError('go', goPackages);

function accumList(set: Record<string, string[]>, key: string, value: string) {
if (!set[key]) {
set[key] = [];
}
set[key].push(value);
}

function maybeError(language: string, set: Record<string, string[]>) {
for (const [namespace, modules] of Object.entries(set)) {
if (modules.length > 1) {
self._diagnostics.push(
JsiiDiagnostic.JSII_4010_SUBMODULE_NAMESPACE_CONFLICT.create(undefined, language, namespace, modules),
);
}
}
}
}
}

export interface AssemblerOptions {
Expand Down
7 changes: 7 additions & 0 deletions src/jsii-diagnostic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,13 @@ export class JsiiDiagnostic implements ts.Diagnostic {
name: 'typescript-config/disabled-tsconfig-validation',
});

public static readonly JSII_4010_SUBMODULE_NAMESPACE_CONFLICT = Code.warning({
code: 4010,
formatter: (language: string, namespace: string, modules: string[]) =>
`Multiple modules emit to the same ${language} namespace "${namespace}": ${modules.join(', ')}`,
name: 'jsii-config/submodule-conflict',
});

//////////////////////////////////////////////////////////////////////////////
// 5000 => 5999 -- LANGUAGE COMPATIBILITY ERRORS

Expand Down
20 changes: 20 additions & 0 deletions test/submodules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ test('submodules loaded from directories can have targets', () => {
);
});

test('submodules loaded from files can have targets', () => {
const assembly = sourceToAssemblyHelper({
'index.ts': 'export * as submodule from "./subfile"',
'subfile.ts': 'export class Foo { }',
'.subfile.jsiirc.json': JSON.stringify({
targets: {
python: 'fun',
},
}),
});

expect(assembly.submodules!['testpkg.submodule']).toEqual(
expect.objectContaining({
targets: {
python: 'fun',
},
}),
);
});

test('submodule READMEs can have literate source references', () => {
const assembly = sourceToAssemblyHelper({
'index.ts': 'export * as submodule from "./subdir"',
Expand Down