Skip to content

Instantly share code, notes, and snippets.

@supermamon
Last active January 10, 2022 19:41
Show Gist options
  • Save supermamon/d66ec2357598a948d1561ac9e2aaa0c5 to your computer and use it in GitHub Desktop.
Save supermamon/d66ec2357598a948d1561ac9e2aaa0c5 to your computer and use it in GitHub Desktop.
port of node's `require` for Scriptable.app
const require = importModule('scriptable-require')
const moment = await require('moment', true)
log(moment().format('dddd'))
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: magic;
// shared by hwangbible
// at: https://talk.automators.fm/t/tips-how-to-use-npm-modules-in-scriptable/5747
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}`);
};
@supermamon
Copy link
Author

Thanks for the reference. I've updated the script to mention the author and thread.

@hkE29o
Copy link

hkE29o commented Jan 10, 2022

Thank you for sharing with us. When running the example on my iPad I get the following error:
Error: Data could not be read, they are not in the right format. (Translated from German)
Any ideas why this is the case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment