150 lines
No EOL
4.9 KiB
JavaScript
150 lines
No EOL
4.9 KiB
JavaScript
// Variables used by Scriptable.
|
|
// These must be at the very top of the file. Do not edit.
|
|
// icon-color: deep-blue; icon-glyph: magic;
|
|
// Variables used by Scriptable.
|
|
// These must be at the very top of the file. Do not edit.
|
|
// icon-color: gray; icon-glyph: magic;
|
|
const fm = FileManager.iCloud();
|
|
const dir = fm.documentsDirectory();
|
|
|
|
const downloadIfNeeded = async (pkg, isAutoUpdateOn) => {
|
|
let name = getPackageName(pkg);
|
|
let filePath = fm.joinPath(dir, name + '.js');
|
|
let isInstalled = await isFound(filePath);
|
|
|
|
// If the package exists and autoupdate is off, stop checking further
|
|
if (isInstalled && !isAutoUpdateOn) {
|
|
console.log(`'${name}' is already installed, and autoupdate is disabled! Proceeding to import from disk...`);
|
|
return;
|
|
}
|
|
|
|
// Get the package information which satisfies the given semver range
|
|
let versionInfo = await getStatus(pkg);
|
|
let versions = versionInfo.satisfied;
|
|
let version = versionInfo.highest;
|
|
|
|
// Download the newer version if necessary
|
|
if (isInstalled && isAutoUpdateOn) {
|
|
let installedVersion = await getInstalledVersion(name);
|
|
// Check if the installed version satisfies the semver range
|
|
if (versions.includes(installedVersion)) {
|
|
console.log(`'${name}@${installedVersion}' satisfies the requested version. Good to go!`);
|
|
return;
|
|
} else {
|
|
console.log(`'${name}@${installedVersion}' doesn't match the version requested. Reinstalling '${version}' now...`);
|
|
}
|
|
} else {
|
|
console.log(`'${name}' was never installed previously. Downloading now...`);
|
|
}
|
|
|
|
// Download the package source and save to disk
|
|
let source = await getPackageSource(pkg);
|
|
savePackageToDisk(name, version, source);
|
|
};
|
|
|
|
const getInstalledVersion = async name => {
|
|
// Read the version from {package}.ver
|
|
let filePath = fm.joinPath(dir, name + '.ver');
|
|
let version;
|
|
if (isFound(filePath)) {
|
|
let content = fm.readString(filePath);
|
|
if (/^\d+\.\d+\.\d+$/g.test(content)) {
|
|
version = content;
|
|
}
|
|
}
|
|
console.log(`The installed version of '${name}' is ${version}.`);
|
|
return version;
|
|
};
|
|
|
|
const getPackageSource = async pkg => {
|
|
// Get the standalone package source from wzrd.in
|
|
let request = new Request(`https://wzrd.in/standalone/${encodeURIComponent(pkg)}`);
|
|
let response = await request.loadString();
|
|
return response;
|
|
};
|
|
|
|
const getPackageName = pkg => {
|
|
return pkg.split('@')[0];
|
|
};
|
|
|
|
const getStatus = async pkg => {
|
|
// Retrieve the information about the package
|
|
let request = new Request(`https://wzrd.in/status/${encodeURIComponent(pkg)}`);
|
|
let response = await request.loadJSON();
|
|
|
|
// Fail if the response is not good
|
|
if (response.statusCode >= 400 || response.ok === false) {
|
|
throw response.message;
|
|
}
|
|
|
|
// Fail if the semver did not satisfy any versions available on npm
|
|
// Otherwise, sort the versions in descending order
|
|
let versions = response.builds && Object.keys(response.builds);
|
|
if (versions.length < 1) {
|
|
throw `'${pkg}' did not satisfy any versions available on npm!`;
|
|
} else {
|
|
versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
|
}
|
|
|
|
// Get all the satisfied versions and the highest version
|
|
let result = {
|
|
highest: versions[0],
|
|
satisfied: versions,
|
|
};
|
|
|
|
return result;
|
|
};
|
|
|
|
const isFound = async filePath => {
|
|
// Check if the package is already downloaded
|
|
if (fm.fileExists(filePath)) {
|
|
return true;
|
|
}
|
|
|
|
// Sync with iCloud and check again
|
|
await syncFileWithiCloud(filePath);
|
|
if (fm.fileExists(filePath)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const savePackageToDisk = (name, version, source) => {
|
|
// Write the package source and version info to disk
|
|
let filename = fm.joinPath(dir, name);
|
|
let jsFilePath = filename + '.js';
|
|
let versionFilePath = filename + '.ver';
|
|
let pkg = `${name}@${version}`;
|
|
tryWriteFile(jsFilePath, source, pkg);
|
|
tryWriteFile(versionFilePath, version, pkg);
|
|
console.log(`Successfully installed ${name}@${version}!`);
|
|
};
|
|
|
|
const syncFileWithiCloud = async filePath => {
|
|
// Try to sync with iCloud in case the package exists only on iCloud
|
|
try {
|
|
console.log(`Attempting to sync with iCloud just in case...`);
|
|
await fm.downloadFileFromiCloud(filePath);
|
|
console.log(`Finished syncing ${filePath}`);
|
|
} catch (err) {
|
|
console.log(`${filePath} does not exist on iCloud.`);
|
|
}
|
|
};
|
|
|
|
const tryWriteFile = (path, content, pkg) => {
|
|
// Sometimes wzrd.in is acting up and the file content is undefined.
|
|
// So, here is a little trick to let you know what's going on.
|
|
try {
|
|
console.log(`Saving ${pkg} to disk at ${path}...`);
|
|
fm.writeString(path, content);
|
|
} catch (err) {
|
|
throw `The package source from 'https://wzrd.in/standalone/${pkg}' is probably corrupted! Try with the different patch version.`;
|
|
}
|
|
};
|
|
|
|
module.exports = async (pkg, isAutoUpdateOn = false) => {
|
|
let name = getPackageName(pkg);
|
|
await downloadIfNeeded(pkg, isAutoUpdateOn);
|
|
return importModule(`${name}`);
|
|
}; |