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.

Revisions

  1. supermamon revised this gist Oct 2, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions scriptable-require.js
    Original file line number Diff line number Diff line change
    @@ -2,8 +2,8 @@
    // These must be at the very top of the file. Do not edit.
    // icon-color: red; icon-glyph: magic;

    // I forgot where I found this.
    // If you made this, let me know so I can at least credit your here.
    // 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();
  2. supermamon revised this gist Dec 1, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion require-example.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@

    const require = importModule('npm-require')
    const require = importModule('scriptable-require')
    const moment = await require('moment', true)
    log(moment().format('dddd'))
  3. supermamon created this gist Dec 1, 2020.
    4 changes: 4 additions & 0 deletions require-example.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,4 @@

    const require = importModule('npm-require')
    const moment = await require('moment', true)
    log(moment().format('dddd'))
    151 changes: 151 additions & 0 deletions scriptable-require.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,151 @@
    // Variables used by Scriptable.
    // These must be at the very top of the file. Do not edit.
    // icon-color: red; icon-glyph: magic;

    // I forgot where I found this.
    // If you made this, let me know so I can at least credit your here.

    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}`);
    };