Skip to content

Commit

Permalink
feat(spm): Copy plugins and rewrite Cordova dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
dpogue committed Jan 18, 2025
1 parent 14e30aa commit b92ceaa
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 28 deletions.
8 changes: 3 additions & 5 deletions lib/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,8 @@ class Api {
.addPlugin(plugin, installOptions)
.then(() => {
if (plugin != null && isSwiftPackagePlugin(plugin)) {
const packagePath = path.join(this.locations.root, 'CordovaPlugins', 'Package.swift');
const spm = new SwiftPackage(packagePath);
spm.addPlugin(plugin);
const spm = new SwiftPackage(this.locations.root);
spm.addPlugin(plugin, installOptions);
}
})
.then(() => {
Expand Down Expand Up @@ -293,8 +292,7 @@ class Api {
.removePlugin(plugin, uninstallOptions)
.then(() => {
if (plugin != null && isSwiftPackagePlugin(plugin)) {
const packagePath = path.join(this.locations.root, 'CordovaPlugins', 'Package.swift');
const spm = new SwiftPackage(packagePath);
const spm = new SwiftPackage(this.locations.root);
spm.removePlugin(plugin);
}
})
Expand Down
43 changes: 36 additions & 7 deletions lib/SwiftPackage.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,67 @@
*/

const fs = require('node:fs');
const path = require('node:path');
const CordovaError = require('cordova-common').CordovaError;

class SwiftPackage {
constructor (packagePath) {
this.path = packagePath;
constructor (projectRoot) {
this.root = projectRoot;
this.path = path.join(this.root, 'CordovaPlugins', 'Package.swift');

if (!fs.existsSync(this.path)) {
throw new CordovaError('Package.swift is not found.');
}
}

_pluginReference (plugin) {
_pluginReference (plugin, opts = {}) {
let pluginPath = path.relative(path.dirname(this.path), path.join(this.root, 'packages', plugin.id));
if (opts.link) {
pluginPath = path.relative(path.dirname(this.path), plugin.dir);
}

return `
package.dependencies.append(.package(name: "${plugin.id}", path: "../../../plugins/${plugin.id}"))
package.dependencies.append(.package(name: "${plugin.id}", path: "${pluginPath.replaceAll(path.sep, path.posix.sep)}"))
package.targets.first?.dependencies.append(.product(name: "${plugin.id}", package: "${plugin.id}"))
`;
}

addPlugin (plugin) {
addPlugin (plugin, opts = {}) {
if (!opts.link) {
// Copy the plugin into the packages directory
fs.cpSync(plugin.dir, path.join(this.root, 'packages', plugin.id), { recursive: true });

const pkgSwiftPath = path.join(this.root, 'packages', plugin.id, 'Package.swift');
const pkg_fd = fs.openSync(pkgSwiftPath, 'r+');

let packageContent = fs.readFileSync(pkg_fd, 'utf8');
packageContent = packageContent.replace(/\(.*url.+cordova-ios.+\)/gm, '(name: "cordova-ios", path: "../cordova-ios")');

fs.ftruncateSync(pkg_fd);
fs.writeSync(pkg_fd, packageContent, 0, 'utf8');
fs.closeSync(pkg_fd);
}

const fd = fs.openSync(this.path, 'a');
fs.writeFileSync(fd, this._pluginReference(plugin), 'utf8');
fs.writeFileSync(fd, this._pluginReference(plugin, opts), 'utf8');
fs.closeSync(fd);
}

removePlugin (plugin) {
const fd = fs.openSync(this.path, 'r+');

if (fs.existsSync(path.join(this.root, 'packages', plugin.id))) {
fs.rmSync(path.join(this.root, 'packages', plugin.id), { recursive: true, force: true });
}

let packageContent = fs.readFileSync(fd, 'utf8');

// We don't know if it was originally linked or not, so try to remove both
packageContent = packageContent.replace(this._pluginReference(plugin), '');
packageContent = packageContent.replace(this._pluginReference(plugin, { link: true }), '');

fs.ftruncateSync(fd);
fs.writeFileSync(fd, packageContent, 'utf8');
fs.writeSync(fd, packageContent, 0, 'utf8');
fs.closeSync(fd);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/spec/unit/Api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ describe('Platform Api', () => {
it('should add the plugin reference to Package.swift', () => {
return api.addPlugin(swift_plugin)
.then(() => {
expect(swiftPackage_mock.addPlugin).toHaveBeenCalledWith(swift_plugin);
expect(swiftPackage_mock.addPlugin).toHaveBeenCalledWith(swift_plugin, jasmine.any(Object));
});
});
});
Expand Down
76 changes: 61 additions & 15 deletions tests/spec/unit/SwiftPackage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,56 +27,102 @@ tmp.setGracefulCleanup();
const fixturePackage = fs.readFileSync(path.join(__dirname, 'fixtures', 'test-Package.swift'), 'utf-8');

describe('SwiftPackage', () => {
let tmpDir;
beforeEach(() => {
tmpDir = tmp.dirSync();
});

afterEach(() => {
fs.rmSync(tmpDir.name, { recursive: true, force: true });
});

it('should error if Package.swift file does not exist', () => {
expect(() => {
const _ = new SwiftPackage('dummypath');
const _ = new SwiftPackage(tmpDir.name);
expect(_).not.toEqual(null); // To avoid ESLINT error "Do not use 'new' for side effects"
}).toThrow();
});

describe('addPlugin', () => {
const my_plugin = {
id: 'my-plugin'
id: 'my-plugin',
dir: path.join(__dirname, 'fixtures', 'org.test.plugins.swiftpackageplugin')
};

let pkg;
let tmpFile;
beforeEach(() => {
tmpFile = tmp.fileSync({ discardDescriptor: true });
fs.writeFileSync(tmpFile.name, fixturePackage, 'utf8');
fs.mkdirSync(path.join(tmpDir.name, 'CordovaPlugins'));
fs.writeFileSync(path.join(tmpDir.name, 'CordovaPlugins', 'Package.swift'), fixturePackage, 'utf8');

pkg = new SwiftPackage(tmpFile.name);
pkg = new SwiftPackage(tmpDir.name);
});

it('should add plugin references to the package file', () => {
pkg.addPlugin(my_plugin);

const content = fs.readFileSync(tmpFile.name, 'utf8');
expect(content).toContain('.package(name: "my-plugin"');
const pkgPath = path.join(tmpDir.name, 'CordovaPlugins', 'Package.swift');
const content = fs.readFileSync(pkgPath, 'utf8');
expect(content).toContain('.package(name: "my-plugin", path: "../packages/my-plugin")');
expect(content).toContain('.product(name: "my-plugin", package: "my-plugin")');
});

it('should copy the plugin into the packages directory', () => {
pkg.addPlugin(my_plugin);

expect(fs.existsSync(path.join(tmpDir.name, 'packages', 'my-plugin'))).toBeTruthy();
});

it('should add plugin references to the package file when linked', () => {
pkg.addPlugin(my_plugin, { link: true });

const pkgPath = path.join(tmpDir.name, 'CordovaPlugins', 'Package.swift');
const content = fs.readFileSync(pkgPath, 'utf8');

expect(content).toContain('.package(name: "my-plugin", path: "');
expect(content).not.toContain('.package(name: "my-plugin", path: "../packages/my-plugin")');
expect(content).toContain('.product(name: "my-plugin", package: "my-plugin")');
});

it('should copy a linked plugin into the packages directory', () => {
pkg.addPlugin(my_plugin, { link: true });

expect(fs.existsSync(path.join(tmpDir.name, 'packages', 'my-plugin'))).toBeFalsy();
});
});

describe('removePlugin', () => {
const my_plugin = {
id: 'my-plugin'
id: 'my-plugin',
dir: path.join(__dirname, 'fixtures', 'org.test.plugins.swiftpackageplugin')
};

let pkg;
let tmpFile;
beforeEach(() => {
tmpFile = tmp.fileSync({ discardDescriptor: true });
fs.mkdirSync(path.join(tmpDir.name, 'CordovaPlugins'));
const pkgPath = path.join(tmpDir.name, 'CordovaPlugins', 'Package.swift');
fs.writeFileSync(pkgPath, fixturePackage, 'utf8');

pkg = new SwiftPackage(tmpFile.name);
fs.writeFileSync(tmpFile.name, fixturePackage + pkg._pluginReference(my_plugin), 'utf8');
pkg = new SwiftPackage(tmpDir.name);
fs.writeFileSync(pkgPath, fixturePackage + pkg._pluginReference(my_plugin), 'utf8');
});

it('should add plugin references to the package file', () => {
it('should remove plugin references to the package file', () => {
pkg.removePlugin(my_plugin);

const content = fs.readFileSync(tmpFile.name, 'utf8');
const content = fs.readFileSync(path.join(tmpDir.name, 'CordovaPlugins', 'Package.swift'), 'utf8');
expect(content).not.toContain('.package(name: "my-plugin"');
expect(content).not.toContain('.product(name: "my-plugin", package: "my-plugin")');
});

it('should remove the plugin from the packages directory', () => {
fs.mkdirSync(path.join(tmpDir.name, 'packages', 'my-plugin'), { recursive: true });
fs.writeFileSync(path.join(tmpDir.name, 'packages', 'my-plugin', 'test.txt'), 'Test', 'utf-8');

expect(fs.existsSync(path.join(tmpDir.name, 'packages', 'my-plugin'))).toBeTruthy();

pkg.removePlugin(my_plugin);

expect(fs.existsSync(path.join(tmpDir.name, 'packages', 'my-plugin'))).toBeFalsy();
});
});
});

0 comments on commit b92ceaa

Please sign in to comment.