1
0
mirror of https://github.com/HorlogeSkynet/CDNUpdates synced 2025-05-12 04:00:19 +02:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Samuel FORESTIER
8f3facd076 Minor optimizations for static iterables 2020-06-23 13:09:19 +02:00
Samuel FORESTIER
a81ff7ebe6 Sets compare_with_[...] methods as "private" in CDNContent module 2020-06-23 13:02:42 +02:00
Samuel FORESTIER
8cc7d976b6 Regular test cases updates (as of June 2020) 2020-06-23 13:00:28 +02:00
Samuel FORESTIER
42ffbed4f6 Fixes a regression introduced in v1.2.0, details :
* Fixes crashes occurring on files loading CDN from :
	+ code.jquery.com
	+ cdn.rawgit.com
	+ ajax.{aspnetcdn,microsoft}.com
* Fixes potential crashes on links not containing any semantic versions
* Minor optimizations with REGEXP pattern compilation
* Code styles
2020-06-23 13:00:09 +02:00
Samuel FORESTIER
84065165af Specifies the GitHub API token required scope in README 2020-06-23 12:52:25 +02:00
Samuel FORESTIER
5f9f446e41 "You should **explicitly** load [...] over HTTPS !" 2020-06-23 11:57:32 +02:00
Samuel FORESTIER
c6655a2d5f Minor code cleanup and styles 2020-06-23 11:27:52 +02:00
8 changed files with 79 additions and 68 deletions

@ -2,8 +2,8 @@
from urllib.parse import urlparse
from .CDNConstants import CDN_PROVIDERS
from .CDNContent import CDNContent
from .CDNConstants import CDN_PROVIDERS
from .CDNUtils import log_message

@ -2,7 +2,7 @@
from sublime import IGNORECASE
from .CDNConstants import LINK_REGEX
from .CDNConstants import LINK_REGEXP_PATTERN
class CheckForLinks: # pylint: disable=too-few-public-methods
@ -15,6 +15,6 @@ class CheckForLinks: # pylint: disable=too-few-public-methods
self.view = view
self.region_list = region_list
for region in self.view.find_all(LINK_REGEX, IGNORECASE):
for region in self.view.find_all(LINK_REGEXP_PATTERN, IGNORECASE):
# We have to fill the list directly (passed by reference)
self.region_list.append(region)

@ -8,9 +8,10 @@ from sublime import (
DRAW_EMPTY_AS_OVERWRITE,
DRAW_NO_FILL,
DRAW_NO_OUTLINE,
DRAW_SOLID_UNDERLINE
DRAW_SOLID_UNDERLINE,
LAYOUT_BLOCK,
message_dialog
)
from sublime import LAYOUT_BLOCK, message_dialog
from .CDNUtils import log_message
@ -111,7 +112,7 @@ class CheckForUpdates(Thread):
}}
</style>
<div class="https_warning">
You should load <b>{0}</b> over HTTPS !
You should explicitly load <b>{0}</b> over HTTPS !
</div>
</body>
""".format(
@ -123,7 +124,7 @@ class CheckForUpdates(Thread):
# Let's add some regions for the user with specific icons.
# This is done afterwards to reduce the number of regions drawn.
for status in ['up_to_date', 'to_update', 'not_found']:
for status in ('up_to_date', 'to_update', 'not_found'):
self.view.add_regions(
status,
[cdn_content.sublime_region for cdn_content in self.cdn_content_list

@ -1,14 +1,21 @@
"""CDNUpdates' constants module"""
# pylint: disable=line-too-long
# This regex has been written by @sindresorhus for Semver.
# <https://github.com/sindresorhus/semver-regex> (v3.1.1)
SEMVER_REGEX = r"(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(?=$|\s)"
import re
# This is a regex written by @diegoperini, ported for Python by @adamrofer.
# pylint: disable=line-too-long
# This Semver regular expression has been written by @sindresorhus for NodeJS.
# It has been adapted to remove the starting non-fixed width look-behind (incompatible) and trailing positive look-ahead (useless here).
# <https://github.com/sindresorhus/semver-regex> (v3.1.1)
SEMVER_REGEXP_OBJECT = re.compile(
r"v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?",
re.IGNORECASE
)
# This is a regular expression written by @diegoperini, and ported for Python by @adamrofer.
# <https://gist.github.com/dperini/729294>
# It has been tweaked to work with network path references and HTML tags.
LINK_REGEX = r"(?:(https?:)?//)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S[^\"\s]*)?"
LINK_REGEXP_PATTERN = r"(?:(https?:)?//)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S[^\"\s]*)?"
# pylint: enable=line-too-long
# This list stores the API links of handled CDN providers.

@ -13,7 +13,7 @@ from .CDNConstants import (
CDN_STATIC_FILE_CORRESPONDENCES,
MAXCDN_BOOTSTRAP_CORRESPONDENCES,
OPENSOURCE_KEYCDN_CORRESPONDENCES,
SEMVER_REGEX
SEMVER_REGEXP_OBJECT
)
from .CDNUtils import log_message
@ -54,7 +54,7 @@ class CDNContent:
tmp = self.parsed_result.path.split('/')
self.name = tmp[3]
self.compare_with_latest_cdnjs_version(self.name, tmp[4])
self._compare_with_latest_cdnjs_version(self.name, tmp[4])
elif self.parsed_result.netloc == 'maxcdn.bootstrapcdn.com':
tmp = self.parsed_result.path.split('/')
@ -64,7 +64,7 @@ class CDNContent:
self.status = 'not_found'
return
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
MAXCDN_BOOTSTRAP_CORRESPONDENCES.get(self.name)['owner'],
MAXCDN_BOOTSTRAP_CORRESPONDENCES.get(self.name)['name'],
tmp[2]
@ -76,25 +76,26 @@ class CDNContent:
if tmp[1].startswith('jquery'):
self.name = 'jquery'
version = re.search(SEMVER_REGEX, tmp[1]).group(0)
elif tmp[1] in ['ui', 'mobile', 'color']:
version = SEMVER_REGEXP_OBJECT.search(tmp[1])
version = version and version.group(0)
elif tmp[1] in ('ui', 'mobile', 'color'):
self.name = 'jquery-' + tmp[1]
version = tmp[2]
elif tmp[1] == 'qunit':
self.name = 'qunit'
version = re.search(SEMVER_REGEX, tmp[2]).group(0)
version = SEMVER_REGEXP_OBJECT.search(tmp[2])
version = version and version.group(0)
elif tmp[1] == 'pep':
self.name = 'PEP'
version = tmp[2]
else:
version = None
if not version:
self.status = 'not_found'
return
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
# Only `QUnit` belongs to another organization.
('qunitjs' if self.name == 'qunit' else 'jquery'),
self.name,
@ -109,7 +110,7 @@ class CDNContent:
self.status = 'not_found'
return
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
AJAX_GOOGLE_APIS_CORRESPONDENCES.get(self.name)['owner'],
AJAX_GOOGLE_APIS_CORRESPONDENCES.get(self.name)['name'],
tmp[4]
@ -117,33 +118,31 @@ class CDNContent:
# CDN from CDN.JSDLIVR.NET will be handled here.
elif self.parsed_result.netloc == 'cdn.jsdelivr.net':
"""
The API from JSDLIVR is powerful.
It implies we compute a "fuzzy" version checking.
For instance : "jquery@3" is OK for '3.2.1'.
"""
# The API from JSDLIVR is powerful.
# It implies we compute a "fuzzy" version checking.
# For instance : "jquery@3" is OK for '3.2.1'.
tmp = self.parsed_result.path.split('/')
try:
if tmp[1] == 'npm':
self.name, version = tmp[2].split('@')
self.compare_with_npmjs_version(self.name, version)
self._compare_with_npmjs_version(self.name, version)
elif tmp[1] == 'gh':
self.name, version = tmp[3].split('@')
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
tmp[2], self.name, version,
fuzzy_check=True
)
elif tmp[1] == 'wp':
# This how we'll handle the latest version references, as :
# (https://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js)
# <https://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js>
if len(tmp) < 6:
raise IndexError
self.name = tmp[2]
self.compare_with_latest_wpsvn_tag(self.name, tmp[4])
self._compare_with_latest_wpsvn_tag(self.name, tmp[4])
else:
self.status = 'not_found'
@ -161,17 +160,16 @@ class CDNContent:
# If no semantic version is specified in the URL, we assume either:
# * The developer uses the latest version available (`master`) [OR]
# * The developer knows what he is doing (commit hash specified)
if re.search(SEMVER_REGEX, tmp[3]) is None:
if not SEMVER_REGEXP_OBJECT.search(tmp[3]):
self.status = 'up_to_date'
else:
# If not, we compare this version with the latest tag !
self.compare_with_latest_github_tag(tmp[1], self.name, tmp[3])
self._compare_with_latest_github_tag(tmp[1], self.name, tmp[3])
elif self.parsed_result.netloc == 'code.ionicframework.com':
tmp = self.parsed_result.path.split('/')
self.name = tmp[1]
self.compare_with_latest_github_release('ionic-team', self.name, tmp[2])
self._compare_with_latest_github_release('ionic-team', self.name, tmp[2])
elif self.parsed_result.netloc == 'use.fontawesome.com':
self.name = 'Font Awesome'
@ -187,7 +185,7 @@ class CDNContent:
self.status = 'not_found'
return
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
OPENSOURCE_KEYCDN_CORRESPONDENCES.get(self.name)['owner'],
OPENSOURCE_KEYCDN_CORRESPONDENCES.get(self.name)['name'],
tmp[2]
@ -201,26 +199,32 @@ class CDNContent:
self.status = 'not_found'
return
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
CDN_STATIC_FILE_CORRESPONDENCES.get(self.name)['owner'],
CDN_STATIC_FILE_CORRESPONDENCES.get(self.name)['name'],
tmp[2]
)
elif self.parsed_result.netloc in ['ajax.microsoft.com', 'ajax.aspnetcdn.com']:
elif self.parsed_result.netloc in ('ajax.microsoft.com', 'ajax.aspnetcdn.com'):
tmp = self.parsed_result.path.split('/')
if tmp[2] not in AJAX_MICROSOFT_CORRESPONDENCES.keys():
# Sometimes the version is in the path...
if len(tmp) == 5:
version = tmp[3]
# ... and some other times contained within the name.
else:
version = SEMVER_REGEXP_OBJECT.search(tmp[3])
version = version and version.group(0)
if tmp[2] not in AJAX_MICROSOFT_CORRESPONDENCES.keys() or not version:
self.status = 'not_found'
return
self.name = tmp[2]
self.compare_with_latest_github_tag(
self._compare_with_latest_github_tag(
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2])['owner'],
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2])['name'],
# Sometimes the version is in the path...
tmp[3] if len(tmp) == 5
# ... and some other times contained within the name.
else re.search(SEMVER_REGEX, tmp[3]).group(0),
version,
# Microsoft has tagged some libraries very badly...
# Check `CDNConstants.AJAX_MICROSOFT_CORRESPONDENCES` for this entry.
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2]).get('fuzzy_check', False)
@ -230,9 +234,9 @@ class CDNContent:
tmp = self.parsed_result.path.split('/')
if tmp[1] == 'ckeditor5' and \
tmp[3] in ['classic', 'inline', 'balloon']:
tmp[3] in ('classic', 'inline', 'balloon'):
self.name = "{0} ({1})".format(tmp[1], tmp[3])
self.compare_with_latest_github_release('ckeditor', tmp[1], tmp[2])
self._compare_with_latest_github_release('ckeditor', tmp[1], tmp[2])
else:
self.status = 'not_found'
@ -243,7 +247,7 @@ class CDNContent:
else:
log_message("This statement should not be reached.")
def compare_with_latest_cdnjs_version(self, name, version):
def _compare_with_latest_cdnjs_version(self, name, version):
"""This method handles call and result comparison with the CDNJS' API"""
# We ask CDNJS API to retrieve information about this library.
@ -285,7 +289,7 @@ class CDNContent:
)
)
def compare_with_latest_github_tag(
def _compare_with_latest_github_tag(
self,
owner, name, version,
fuzzy_check=False):
@ -327,7 +331,7 @@ class CDNContent:
)
)
def compare_with_latest_github_release(
def _compare_with_latest_github_release(
self,
owner, name, version,
fuzzy_check=False):
@ -367,7 +371,7 @@ class CDNContent:
)
)
def compare_with_npmjs_version(self, name, version):
def _compare_with_npmjs_version(self, name, version):
"""This method handles call and result comparison with the NPMJS' API"""
request = urlopen(Request(
"https://api.npms.io/v2/search?q={name}".format(name=quote(name)),
@ -402,7 +406,7 @@ class CDNContent:
)
)
def compare_with_latest_wpsvn_tag(self, name, version):
def _compare_with_latest_wpsvn_tag(self, name, version):
"""This method parses HTML from WordPress' SVN plugin page to retrieve the latest tag"""
request = urlopen(
"https://plugins.svn.wordpress.org/{name}/tags/".format(

@ -1,7 +1,6 @@
"""CDNUpdates main class"""
from sublime import error_message
from sublime_plugin import EventListener, TextCommand
from .CDNCheckForCDNProviders import CheckForCDNProviders
@ -27,10 +26,10 @@ class CDNUpdatesCommand(TextCommand): # pylint: disable=too-few-public-methods
"""
# First we check if the current sheet is not still being loaded.
if self.view.is_loading():
error_message("This file is not fully loaded yet.")
error_message("This view is not fully loaded yet.")
return
# If it's OK, we clear the view from the elements added previously.
# If it's OK, we clear the view from the previously added elements.
clear_view(self.view)
self.view.set_status(

@ -48,7 +48,7 @@ Package Control dedicated page [here](https://packagecontrol.io/packages/CDNUpda
Most of the CDN providers don't provide any API for their service, so it would be very tricky to retrieve latest version available directly from them.
Unless for <https://cdnjs.com/>, this plugin is actually based on the GitHub API to fetch the latest existing Git tag directly from the repositories. Its `name` is compared afterwards with the CDN version present in your sources.
If you have many many CDNs in your sheets (or if you want to contribute to this project 😜), you'll surely need to set a GitHub API token to avoid being blocked by the rate limit.
You can generate one [here](https://github.com/settings/tokens), and paste in under the plugin preferences (accessible from `CDNUpdates`'s Sublime menu).
You can generate one [here](https://github.com/settings/tokens) (`public_repo` scope), and paste in under the plugin preferences (accessible from `CDNUpdates`'s Sublime menu).
## CDN Providers currently handled
@ -71,7 +71,7 @@ You can generate one [here](https://github.com/settings/tokens), and paste in un
> It'll be done automatically next time you'll save your sheet :wink:
### I've updated my links, but the _Phantom_ objects don't want to leave...
### I've updated my links, how can I get rid of these _Phantom_ objects ?
> Same as above :smile:

@ -23,23 +23,23 @@ https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.1/css/bootstrap.min.css
https://maxcdn.bootstrapcdn.com/does_not_exist/1.2.3/js/test.min.js
<!-- JQUERY with JQuery -->
https://code.jquery.com/jquery-3.4.1.slim.min.js
https://code.jquery.com/jquery-3.5.1.slim.min.js
https://code.jquery.com/ui/1.11.4/jquery-ui.js
https://code.jquery.com/does_not_exist/1.2.3/pep.js
<!-- GOOGLEAPIS -->
https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
https://ajax.googleapis.com/ajax/libs/shaka-player/2.2.8/shaka-player.compiled.js
https://ajax.googleapis.com/ajax/libs/does_not_exist/1.2.3/swfobject.js
<!-- JSDELIVR... -->
<!-- ...with GitHub -->
https://cdn.jsdelivr.net/gh/jquery/jquery@3.4.1/dist/jquery.min.js
https://cdn.jsdelivr.net/gh/jquery/jquery@3.5.1/dist/jquery.min.js
https://cdn.jsdelivr.net/gh/jquery/jquery@3.1/dist/jquery.min.js
https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js
https://cdn.jsdelivr.net/gh/user/does_not_exist@version/file
<!-- ...with NPMJS -->
https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js
https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
https://cdn.jsdelivr.net/npm/jquery@3.1/dist/jquery.min.js
https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
https://cdn.jsdelivr.net/npm/does_not_exist@version/file
@ -51,7 +51,7 @@ https://cdn.jsdelivr.net/wp/does_not_exist/tags/version/file
<!-- GITHUB AS CDN... -->
<!-- ... with RAWGIT -->
https://rawgit.com/jquery/jquery/3.4.1/src/jquery.js
https://rawgit.com/jquery/jquery/3.5.1/src/jquery.js
https://cdn.rawgit.com/jquery/jquery/3.2.0/src/jquery.js
https://cdn.rawgit.com/does_not_exist/does_not_exist/1.2.2/src/test.js
@ -65,23 +65,23 @@ https://use.fontawesome.com/0123456789.js
<!-- KEYCDN... -->
<!-- ... with Pure -->
https://opensource.keycdn.com/pure/1.0.0/pure-min.css
https://opensource.keycdn.com/pure/2.0.3/pure-min.css
<!-- ... with FontAwesome -->
https://opensource.keycdn.com/fontawesome/4.4.1/font-awesome.min.css
<!-- ... with AngularJS -->
https://opensource.keycdn.com/angularjs/1.6.8/angular.min.js
<!-- STATICFILE -->
https://cdn.staticfile.org/jquery/3.4.1/core.js
https://cdn.staticfile.org/jquery/3.5.1/core.js
https://cdn.staticfile.org/vue/2.2.6/vue.js
https://cdn.staticfile.org/does_not_exist/1.2.3/jquery.min.js
<!-- ASPNETCDN -->
https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.1/jquery.validate.js
https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.2/jquery.validate.js
https://ajax.aspnetcdn.com/ajax/jquery.cycle/2.99/jquery.cycle.all.js
https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js
<!-- CKEDITOR -->
https://cdn.ckeditor.com/ckeditor5/17.0.0/classic/ckeditor.js
https://cdn.ckeditor.com/ckeditor5/19.1.1/classic/ckeditor.js
https://cdn.ckeditor.com/ckeditor5/1.0.0-alpha.1/inline/ckeditor.js
https://cdn.ckeditor.com/ckeditor4/1.0.0-alpha.2/balloon/ckeditor.js