mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Cordova: support for local plugins
This commit is contained in:
committed by
Slava Kim
parent
3521fabb60
commit
cd28950846
@@ -396,12 +396,10 @@ var ensureCordovaProject = function (projectContext, appName) {
|
||||
|
||||
// --- Cordova platforms ---
|
||||
|
||||
// Ensures that the Cordova platforms are synchronized with the app-level
|
||||
// platforms.
|
||||
var ensureCordovaPlatforms = function (projectContext) {
|
||||
verboseLog('Ensuring that platforms in cordova build project are in sync');
|
||||
// Get the currently installed platforms from cordova build
|
||||
var getCordovaInstalledPlatforms = function(projectContext) {
|
||||
var cordovaPath = projectContext.getProjectLocalDirectory('cordova-build');
|
||||
var platforms = projectContext.platformList.getCordovaPlatforms();
|
||||
|
||||
var platformsList = execFileSyncOrThrow(
|
||||
localCordova, ['platform', 'list'], { cwd: cordovaPath, env: buildCordovaEnv() });
|
||||
|
||||
@@ -415,9 +413,18 @@ var ensureCordovaPlatforms = function (projectContext) {
|
||||
throw new Error('Failed to parse the output of `cordova platform list`: ' +
|
||||
platformsList.stdout);
|
||||
|
||||
var installedPlatforms = _.map(platformsStrings.split(', '), function (s) {
|
||||
return installedPlatforms = _.map(platformsStrings.split(', '), function (s) {
|
||||
return s.split(' ')[0];
|
||||
});
|
||||
}
|
||||
|
||||
// Ensures that the Cordova platforms are synchronized with the app-level
|
||||
// platforms.
|
||||
var ensureCordovaPlatforms = function (projectContext) {
|
||||
verboseLog('Ensuring that platforms in cordova build project are in sync');
|
||||
var cordovaPath = projectContext.getProjectLocalDirectory('cordova-build');
|
||||
var platforms = projectContext.platformList.getCordovaPlatforms();
|
||||
var installedPlatforms = getCordovaInstalledPlatforms(projectContext);
|
||||
|
||||
_.each(platforms, function (platform) {
|
||||
if (_.contains(installedPlatforms, platform))
|
||||
@@ -464,8 +471,16 @@ var targetsToPlatforms = cordova.targetsToPlatforms = function (targets) {
|
||||
var installPlugin = function (cordovaPath, name, version, conf) {
|
||||
verboseLog('Installing a plugin', name, version);
|
||||
|
||||
var pluginInstallCommand;
|
||||
|
||||
if (utils.isUrlWithFileUri(version)) {
|
||||
// Strip file://
|
||||
pluginInstallCommand = version.substr(7);
|
||||
} else {
|
||||
pluginInstallCommand = version ? name + '@' + version : name;
|
||||
}
|
||||
|
||||
// XXX do something different for plugins fetched from a url.
|
||||
var pluginInstallCommand = version ? name + '@' + version : name;
|
||||
var localPluginsPath = localPluginsPathFromCordovaPath(cordovaPath);
|
||||
|
||||
if (version && utils.isUrlWithSha(version)) {
|
||||
@@ -559,7 +574,7 @@ var writeTarballPluginsLock = function (cordovaPath, tarballPluginsLock) {
|
||||
|
||||
// Returns the list of installed plugins as a hash from plugin name to version.
|
||||
var getInstalledPlugins = function (cordovaPath) {
|
||||
verboseLog('Getting installed plugins for project');
|
||||
verboseLog('Getting installed plugins for project in ' + cordovaPath);
|
||||
var installedPlugins = {};
|
||||
|
||||
var pluginsOutput = execFileSyncOrThrow(localCordova, ['plugin', 'list'],
|
||||
@@ -614,17 +629,30 @@ var ensureCordovaPlugins = function (projectContext, options) {
|
||||
// Due to the dependency structure of Cordova plugins, it is impossible to
|
||||
// upgrade the version on an individual Cordova plugin. Instead, whenever a
|
||||
// new Cordova plugin is added or removed, or its version is changed,
|
||||
// we just reinstall all of the plugins.
|
||||
// we just reinstall all of the plugins. Additionally if there are any plugins
|
||||
// added from the local path, we will reinstall them just to be sure they
|
||||
// are up to date since we do not track changes in their sources.
|
||||
|
||||
var shouldReinstallPlugins = false;
|
||||
var shouldReinstallPlugins = false,
|
||||
shouldReinstallPluginsFromLocalPaths = false,
|
||||
pluginsFromLocalPath = {};
|
||||
|
||||
// Iterate through all of the plugin and find if any of them have a new
|
||||
// version.
|
||||
// Iterate through all of the plugins and find if any of them have a new
|
||||
// version. Additionally check if we have plugins installed from local path.
|
||||
var pluginFromLocalPath;
|
||||
_.each(plugins, function (version, name) {
|
||||
// Check if plugin is installed from local path
|
||||
pluginFromLocalPath = utils.isUrlWithFileUri(version);
|
||||
if (pluginFromLocalPath) {
|
||||
shouldReinstallPluginsFromLocalPaths = true;
|
||||
pluginsFromLocalPath[name] = version;
|
||||
}
|
||||
|
||||
// XXX there is a hack here that never updates a package if you are
|
||||
// trying to install it from a URL, because we can't determine if
|
||||
// it's the right version or not
|
||||
if (! _.has(installedPlugins, name) || installedPlugins[name] !== version) {
|
||||
if (! _.has(installedPlugins, name) ||
|
||||
(installedPlugins[name] !== version && !pluginFromLocalPath)) {
|
||||
// The version of the plugin has changed, or we do not contain a plugin.
|
||||
shouldReinstallPlugins = true;
|
||||
}
|
||||
@@ -638,37 +666,63 @@ var ensureCordovaPlugins = function (projectContext, options) {
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldReinstallPlugins) {
|
||||
if (shouldReinstallPluginsFromLocalPaths)
|
||||
verboseLog('Reinstalling cordova plugins added from the local path');
|
||||
|
||||
if (shouldReinstallPlugins || shouldReinstallPluginsFromLocalPaths) {
|
||||
// Loop through all of the current plugins and remove them one by one until
|
||||
// we have no plugins. It's necessary to loop because we might have
|
||||
// dependencies between plugins.
|
||||
var uninstallAllPlugins = function () {
|
||||
// we have deleted proper amount of plugins. It's necessary to loop because
|
||||
// we might have dependencies between plugins.
|
||||
var uninstallPlugins = function (pluginsToUninstall, clearPluginsDirectory) {
|
||||
installedPlugins = getInstalledPlugins(cordovaPath);
|
||||
while (_.size(installedPlugins)) {
|
||||
_.each(installedPlugins, function (version, name) {
|
||||
uninstallPlugin(cordovaPath, name, utils.isUrlWithSha(version));
|
||||
var uninstalled = 1;
|
||||
// Detect if we couldn't delete any more plugins just to avoid
|
||||
// hanging forever
|
||||
while (uninstalled.length !== 0 && _.size(pluginsToUninstall)) {
|
||||
_.each(pluginsToUninstall, function (version, name) {
|
||||
uninstallPlugin(cordovaPath, name, utils.isUrlWithSha(version));
|
||||
});
|
||||
installedPlugins = getInstalledPlugins(cordovaPath);
|
||||
|
||||
uninstalled = _.difference(
|
||||
Object.keys(pluginsToUninstall), Object.keys(installedPlugins)
|
||||
);
|
||||
pluginsToUninstall = _.omit(pluginsToUninstall, uninstalled);
|
||||
}
|
||||
// XXX HACK, because Cordova doesn't properly clear its plugins on `rm`.
|
||||
// This will completely destroy the project state. We should work with
|
||||
// Cordova to fix the bug in their system, because it doesn't seem
|
||||
// like there's a way around this.
|
||||
files.rm_recursive(files.pathJoin(cordovaPath, 'platforms'));
|
||||
if (clearPluginsDirectory)
|
||||
files.rm_recursive(files.pathJoin(cordovaPath, 'platforms'));
|
||||
|
||||
ensureCordovaPlatforms(projectContext);
|
||||
};
|
||||
|
||||
buildmessage.enterJob({ title: "installing Cordova plugins"}, function () {
|
||||
uninstallAllPlugins();
|
||||
installedPlugins = getInstalledPlugins(cordovaPath);
|
||||
if (shouldReinstallPlugins)
|
||||
uninstallPlugins(installedPlugins, true);
|
||||
else
|
||||
uninstallPlugins(pluginsFromLocalPath, false);
|
||||
|
||||
// Now install all of the plugins.
|
||||
// Now install necessary plugins.
|
||||
try {
|
||||
// XXX: forkJoin with parallel false?
|
||||
var pluginsInstalled = 0;
|
||||
var pluginsInstalled, pluginsToInstall;
|
||||
|
||||
if (shouldReinstallPlugins) {
|
||||
pluginsInstalled = 0;
|
||||
pluginsToInstall = plugins;
|
||||
} else {
|
||||
pluginsInstalled = _.size(installedPlugins);
|
||||
pluginsToInstall = pluginsFromLocalPath;
|
||||
}
|
||||
|
||||
var pluginsCount = _.size(plugins);
|
||||
|
||||
buildmessage.reportProgress({ current: 0, end: pluginsCount });
|
||||
_.each(plugins, function (version, name) {
|
||||
_.each(pluginsToInstall, function (version, name) {
|
||||
installPlugin(cordovaPath, name, version, pluginsConfiguration[name]);
|
||||
|
||||
buildmessage.reportProgress({
|
||||
@@ -680,7 +734,7 @@ var ensureCordovaPlugins = function (projectContext, options) {
|
||||
// If a plugin fails to install, then remove all plugins and throw the
|
||||
// error. Cordova doesn't remove the plugin by default for some reason.
|
||||
// XXX don't throw and improve this error message.
|
||||
uninstallAllPlugins();
|
||||
uninstallPlugins(getInstalledPlugins(cordovaPath), true);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Package.describe({
|
||||
version: "1.0.0",
|
||||
summary: "contains a empty cordova plugin"
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
Cordova.depends({
|
||||
'com.cordova.empty': 'file://../../../../cordova-local-plugin'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
id="com.cordova.empty" version="0.2.3">
|
||||
<name>Empty</name>
|
||||
<description>Cordova Empty Plugin</description>
|
||||
<keywords>cordova,empty</keywords>
|
||||
<repo>https://github.com</repo>
|
||||
|
||||
<js-module src="www/Empty.js" name="Empty">
|
||||
<clobbers target="cordova.plugins.Empty" />
|
||||
</js-module>
|
||||
|
||||
|
||||
<platform name="android">
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="Empty" >
|
||||
<param name="android-package" value="com.cordova.empty.Empty"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/Empty.java" target-dir="src/com/cordova/empty" />
|
||||
</platform>
|
||||
|
||||
</plugin>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.cordova.empty;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class Empty extends CordovaPlugin {
|
||||
public Object onMessage(String id, Object data) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.cordova.empty;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class Empty extends CordovaPlugin {
|
||||
public Object onMessage(String id, Object data) {
|
||||
System.out.println("change");
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
var Empty = {
|
||||
};
|
||||
module.exports = Empty;
|
||||
190
tools/tests/cordova-plugins.js
vendored
190
tools/tests/cordova-plugins.js
vendored
@@ -17,17 +17,15 @@ var copyFile = function(from, to, sand) {
|
||||
sand.write(to, contents);
|
||||
};
|
||||
|
||||
|
||||
var localCordova = files.pathJoin(files.getCurrentToolsDir(), "tools",
|
||||
"cordova-scripts", "cordova.sh");
|
||||
|
||||
|
||||
// Given a sandbox, that has the app as its currend cwd, read the versions file
|
||||
// and check that it contains the plugins that we are looking for. We don't
|
||||
// check the order, we just want to make sure that the right dependencies are
|
||||
// in.
|
||||
// and read the plugins list.
|
||||
//
|
||||
// sand: a sandbox, that has the main app directory as its cwd.
|
||||
// plugins: an array of plugins in order.
|
||||
var checkCordovaPlugins = selftest.markStack(function(sand, plugins) {
|
||||
var getCordovaPluginsList = function(sand) {
|
||||
var lines = selftest.execFileSync(localCordova, ['plugins'],
|
||||
{
|
||||
cwd: files.pathJoin(sand.cwd, '.meteor', 'local', 'cordova-build'),
|
||||
@@ -38,12 +36,24 @@ var checkCordovaPlugins = selftest.markStack(function(sand, plugins) {
|
||||
if (lines[0].match(/No plugins/)) {
|
||||
lines = [];
|
||||
}
|
||||
|
||||
lines.sort();
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Given a sandbox, that has the app as its currend cwd, read the versions file
|
||||
// and check that it contains the plugins that we are looking for. We don't
|
||||
// check the order, we just want to make sure that the right dependencies are
|
||||
// in.
|
||||
//
|
||||
// sand: a sandbox, that has the main app directory as its cwd.
|
||||
// plugins: an array of plugins in order.
|
||||
var checkCordovaPlugins = selftest.markStack(function(sand, plugins) {
|
||||
var cordovaPlugins = getCordovaPluginsList(sand);
|
||||
|
||||
plugins = _.clone(plugins).sort();
|
||||
|
||||
var i = 0;
|
||||
_.each(lines, function(line) {
|
||||
_.each(cordovaPlugins, function(line) {
|
||||
if (!line || line === '') return;
|
||||
// XXX should check for the version as well?
|
||||
selftest.expectEqual(line.split(' ')[0], plugins[i]);
|
||||
@@ -52,6 +62,16 @@ var checkCordovaPlugins = selftest.markStack(function(sand, plugins) {
|
||||
selftest.expectEqual(plugins.length, i);
|
||||
});
|
||||
|
||||
// Like the function above but only looks if a certain plugin is on the list
|
||||
var checkCordovaPluginExists = selftest.markStack(function(sand, plugin) {
|
||||
var cordovaPlugins = getCordovaPluginsList(sand);
|
||||
var found = false;
|
||||
cordovaPlugins = cordovaPlugins.map(function (line) {
|
||||
if (line && line !== '') return line.split(' ')[0];
|
||||
});
|
||||
selftest.expectTrue(_.contains(cordovaPlugins, plugin));
|
||||
});
|
||||
|
||||
// Given a sandbox, that has the app as its cwd, read the cordova plugins
|
||||
// file and check that it contains exactly the plugins specified, in order.
|
||||
//
|
||||
@@ -131,7 +151,6 @@ selftest.define("change cordova plugins", ["cordova"], function () {
|
||||
run.match("restarted");
|
||||
});
|
||||
|
||||
|
||||
// Add plugins through the command line, and make sure that the correct set of
|
||||
// changes is reflected in .meteor/packages, .meteor/versions and list
|
||||
selftest.define("add cordova plugins", ["slow", "cordova"], function () {
|
||||
@@ -194,7 +213,7 @@ selftest.define("add cordova plugins", ["slow", "cordova"], function () {
|
||||
run.match("android");
|
||||
|
||||
run = s.run("build", "../a", "--server", "localhost:3000");
|
||||
run.waitSecs(30);
|
||||
run.waitSecs(60);
|
||||
// This fails because the FB plugin does not compile without additional
|
||||
// configuration for android.
|
||||
run.expectExit(1);
|
||||
@@ -232,6 +251,35 @@ selftest.define("add cordova plugins", ["slow", "cordova"], function () {
|
||||
run.waitSecs(60);
|
||||
run.expectExit(0);
|
||||
checkCordovaPlugins(s, ["org.apache.cordova.device"]);
|
||||
|
||||
run = s.run("remove", "cordova:org.apache.cordova.device");
|
||||
run.match("removed");
|
||||
run.expectExit(0);
|
||||
|
||||
run = s.run("add", "cordova:com.example.plugin@file://");
|
||||
run.matchErr("exact version of dependency");
|
||||
run.expectExit(1);
|
||||
|
||||
run = s.run("add", "cordova:com.example.plugin@file://../../plugin_directory");
|
||||
run.waitSecs(5);
|
||||
run.match("added cordova plugin com.example.plugin");
|
||||
run.expectExit(0);
|
||||
|
||||
checkUserPlugins(s, ["com.example.plugin"]);
|
||||
|
||||
// This should fail beacuse the plugin does not exists at the specified path
|
||||
run = s.run("build", "../a", "--server", "localhost:3000");
|
||||
run.waitSecs(30);
|
||||
run.expectExit(1);
|
||||
|
||||
checkCordovaPlugins(s, []);
|
||||
|
||||
// Add a package with Cordova.depends with local plugin (added from path)
|
||||
run = s.run("add", "empty-cordova-plugin");
|
||||
run.match("added,");
|
||||
run.match("contains a empty cordova plugin");
|
||||
run.expectExit(0);
|
||||
|
||||
});
|
||||
|
||||
selftest.define("remove cordova plugins", function () {
|
||||
@@ -260,6 +308,19 @@ selftest.define("remove cordova plugins", function () {
|
||||
run.match("removed");
|
||||
run.expectExit(1);
|
||||
checkUserPlugins(s, []);
|
||||
|
||||
run = s.run("add", "cordova:com.example.plugin@file://../../plugin_directory");
|
||||
run.waitSecs(5);
|
||||
run.match("added cordova plugin com.example.plugin");
|
||||
run.expectExit(0);
|
||||
checkUserPlugins(s, ["com.example.plugin"]);
|
||||
|
||||
run = s.run("remove", "cordova:com.example.plugin");
|
||||
run.waitSecs(5);
|
||||
run.match("removed");
|
||||
run.expectExit(0);
|
||||
checkUserPlugins(s, []);
|
||||
|
||||
});
|
||||
|
||||
selftest.define("meteor exits when cordova platforms change", ["slow", "cordova"], function () {
|
||||
@@ -328,6 +389,115 @@ selftest.define("meteor exits when cordova platforms change", ["slow", "cordova"
|
||||
run.expectExit(254);
|
||||
});
|
||||
|
||||
selftest.define("meteor reinstalls only local cordova plugins on consecutive builds/runs", ["slow", "cordova"], function () {
|
||||
var s = new Sandbox();
|
||||
var run;
|
||||
|
||||
s.createApp("myapp", "package-tests");
|
||||
s.cd("myapp");
|
||||
|
||||
run = s.run("add-platform", "android");
|
||||
run.match("Do you agree");
|
||||
run.write("Y\n");
|
||||
run.waitSecs(90);
|
||||
run.match("added platform");
|
||||
|
||||
var
|
||||
pluginPath = "../cordova-local-plugin",
|
||||
pluginSource = "packages/empty-cordova-plugin/plugin",
|
||||
androidPluginSource = ".meteor/local/cordova-build/platforms/android/src";
|
||||
|
||||
|
||||
// Copy fake cordova plugin to ../cordova-local-plugin
|
||||
s.mkdir(pluginPath);
|
||||
s.cp(pluginSource + '/plugin.xml', pluginPath + '/plugin.xml');
|
||||
s.mkdir(pluginPath + '/www');
|
||||
s.mkdir(pluginPath + '/src');
|
||||
s.mkdir(pluginPath + '/src/android');
|
||||
s.cp(pluginSource + '/www/Empty.js', pluginPath +'/www/Empty.js');
|
||||
s.cp(
|
||||
pluginSource + '/src/android/Empty.java',
|
||||
pluginPath + '/src/android/Empty.java'
|
||||
);
|
||||
|
||||
// Add the local cordova plugin
|
||||
run = s.run("add", "cordova:com.cordova.empty@file://../../../../cordova-local-plugin");
|
||||
run.waitSecs(60);
|
||||
run.match("added cordova plugin com.cordova.empty");
|
||||
run.expectExit(0);
|
||||
|
||||
checkUserPlugins(s, ["com.cordova.empty"]);
|
||||
|
||||
// Run meteor and check if the cordova android build have the plugin file.
|
||||
// Using "android-device" because "android" would start the simulator.
|
||||
run = s.run("run", "android-device");
|
||||
run.waitSecs(60);
|
||||
run.match("Started your app");
|
||||
run.stop();
|
||||
|
||||
selftest.expectTrue(
|
||||
s.read(
|
||||
androidPluginSource + "/com/cordova/empty/Empty.java"
|
||||
).indexOf('change') === -1
|
||||
);
|
||||
selftest.expectTrue(
|
||||
s.read(
|
||||
androidPluginSource + "/com/cordova/empty/Empty.java"
|
||||
).indexOf('CordovaPlugin') > -1
|
||||
);
|
||||
|
||||
// Copy changed file to the plugin
|
||||
s.cp(
|
||||
pluginSource + '/src/android/Empty_changed.java',
|
||||
pluginPath + '/src/android/Empty.java'
|
||||
);
|
||||
|
||||
// Check if the local plugin will be refreshed
|
||||
run = s.run("run", "android-device");
|
||||
run.waitSecs(60);
|
||||
run.match("Started your app");
|
||||
run.stop();
|
||||
|
||||
selftest.expectTrue(
|
||||
s.read(
|
||||
androidPluginSource + "/com/cordova/empty/Empty.java"
|
||||
).indexOf('change') > -1
|
||||
);
|
||||
|
||||
// Now test the same scenario but with builds
|
||||
s.cp(
|
||||
pluginSource + '/src/android/Empty.java',
|
||||
pluginPath + '/src/android/Empty.java'
|
||||
);
|
||||
|
||||
run = s.run("build", "../a", "--server", "localhost:3000");
|
||||
run.waitSecs(60);
|
||||
run.expectExit(0);
|
||||
|
||||
selftest.expectTrue(
|
||||
s.read(
|
||||
"../a/android/project/src/com/cordova/empty/Empty.java"
|
||||
).indexOf('change') === -1
|
||||
);
|
||||
|
||||
checkCordovaPluginExists(s, "com.cordova.empty");
|
||||
|
||||
s.cp(
|
||||
pluginSource + '/src/android/Empty_changed.java',
|
||||
pluginPath + '/src/android/Empty.java'
|
||||
);
|
||||
|
||||
run = s.run("build", "../a", "--server", "localhost:3000");
|
||||
run.waitSecs(60);
|
||||
run.expectExit(0);
|
||||
|
||||
selftest.expectTrue(
|
||||
s.read(
|
||||
"../a/android/project/src/com/cordova/empty/Empty.java"
|
||||
).indexOf('change') > -1
|
||||
);
|
||||
});
|
||||
|
||||
selftest.define("meteor exits when cordova plugins change", ["slow", "cordova"], function () {
|
||||
var s = new Sandbox();
|
||||
var run;
|
||||
|
||||
@@ -466,6 +466,10 @@ exports.generateSubsetsOfIncreasingSize = function (total, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.isUrlWithFileUri = function (x) {
|
||||
return /^file:\/\/.+/.test(x);
|
||||
};
|
||||
|
||||
exports.isUrlWithSha = function (x) {
|
||||
// For now, just support http/https, which is at least less restrictive than
|
||||
// the old "github only" rule.
|
||||
@@ -491,7 +495,8 @@ exports.ensureOnlyExactVersions = function (dependencies) {
|
||||
});
|
||||
};
|
||||
exports.isExactVersion = function (version) {
|
||||
return semver.valid(version) || exports.isUrlWithSha(version);
|
||||
return semver.valid(version) || exports.isUrlWithSha(version)
|
||||
|| exports.isUrlWithFileUri(version);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user