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
53 changes: 53 additions & 0 deletions packages/react-native/scripts/spm/__tests__/swiftpm-config-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,59 @@ describe('readSwiftpmConfig', () => {
warnSpy.mockRestore();
}
});

it('says nothing when swiftpmConfig mirrors every field of the spm block', () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
try {
const settings = {
name: 'RNSentry',
dependencies: ['react-native-worklets'],
};
const root = makeRoot({name: 'lib', swiftpmConfig: settings});
const config = readSwiftpmConfig(root, {
spm: {name: 'RNSentry', dependencies: ['react-native-worklets']},
});
expect(config).toEqual(settings);
expect(warnSpy).not.toHaveBeenCalled();
} finally {
warnSpy.mockRestore();
}
});

it('still warns about a field the two places disagree on', () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
try {
const root = makeRoot({
name: 'lib',
swiftpmConfig: {name: 'RNSVG', dependencies: ['a']},
});
const config = readSwiftpmConfig(root, {
spm: {name: 'OldName', dependencies: ['a']},
});
expect(config).toEqual({name: 'RNSVG', dependencies: ['a']});
expect(warnSpy).toHaveBeenCalledTimes(1);
const message = warnSpy.mock.calls[0].join(' ');
expect(message).toContain("deprecated 'spm' block (name)");
} finally {
warnSpy.mockRestore();
}
});

it('names only the fields the spm block alone declares', () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
try {
const root = makeRoot({name: 'lib', swiftpmConfig: {name: 'RNSVG'}});
const config = readSwiftpmConfig(root, {
spm: {name: 'RNSVG', dependencies: ['a']},
});
expect(config).toEqual({name: 'RNSVG', dependencies: ['a']});
expect(warnSpy).toHaveBeenCalledTimes(1);
const message = warnSpy.mock.calls[0].join(' ');
expect(message).toContain("deprecated 'spm' block (dependencies)");
} finally {
warnSpy.mockRestore();
}
});
});

describe('readSwiftpmConfig (unknown keys)', () => {
Expand Down
25 changes: 23 additions & 2 deletions packages/react-native/scripts/spm/swiftpm-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'use strict';

const {isValidSwiftName, makeLogger} = require('./spm-utils');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');

Expand Down Expand Up @@ -100,6 +101,20 @@ function declaredFields(
: SWIFTPM_FIELDS.filter(field => config[field] !== undefined);
}

// util.isDeepStrictEqual is missing from the Flow library definitions.
function sameField(
a /*: ?{readonly [string]: unknown} */,
b /*: ?{readonly [string]: unknown} */,
field /*: string */,
) /*: boolean */ {
try {
assert.deepStrictEqual(a?.[field], b?.[field]);
return true;
} catch {
return false;
}
}

function stringList(value /*: unknown */) /*: Array<string> */ {
return Array.isArray(value) ? value.filter(v => typeof v === 'string') : [];
}
Expand Down Expand Up @@ -155,8 +170,14 @@ function readSwiftpmConfig(
}
const deprecated = objectField(rnConfig, 'spm');
const deprecatedFields = declaredFields(deprecated);
if (deprecatedFields.length > 0) {
reportDeprecation(root, deprecatedFields);
// A library that supports React Native versions reading only the `spm`
// block must declare a field in both places, so an identical copy is not
// something it can act on.
const unmirroredFields = deprecatedFields.filter(
field => !sameField(fromPackageJson, deprecated, field),
);
if (unmirroredFields.length > 0) {
reportDeprecation(root, unmirroredFields);
}
if (fromPackageJson == null) {
return deprecatedFields.length > 0 ? deprecated : null;
Expand Down
Loading