ES5 - Parse script version from src path - javascript

I was hoping someone already had this done but what I'm trying to do is output a list of semantic versions of various javascript libraries. Let's assume I have the the following input:
https://code.jquery.com/jquery-2.1.3.js
//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css
https://cdnjs.cloudflare.com/ajax/libs/1140/2.0/1140.css
https://cdnjs.cloudflare.com/ajax/libs/Base64/0.3.0/base64.min.js
https://cdnjs.cloudflare.com/ajax/libs/angular-google-maps/2.1.0-X.10/angular-google-maps.min.js
https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.8-M1/swagger-ui.min.js
https://cdnjs.cloudflare.com/BLAH/BLAH.min.js
I'd like to call a function on each of these strings and have it output the semver from the path:
2.1.3
3.3.4
1140 or 2.0 (prefer)
0.3.0
2.1.0-X.10
2.1.8-M1
null
I was unable to find any existing libraries and was hoping someone had one handy that worked on IE9ish.

I was able to get this to work by using the following code:
it('should parse version from script source', () => {
function getVersion(source:string) {
if (!source) {
return null;
}
var versionRegex = /(v?((\d+)\.(\d+)(\.(\d+))?)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?)/;
var matches = versionRegex.exec(source);
if (matches && matches.length > 0) {
return matches[0];
}
return null;
}
expect(getVersion('https://code.jquery.com/jquery-2.1.3.js')).toBe('2.1.3');
expect(getVersion('//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css')).toBe('3.3.4');
expect(getVersion('https://cdnjs.cloudflare.com/ajax/libs/1140/2.0/1140.css')).toBe('2.0');
expect(getVersion('https://cdnjs.cloudflare.com/ajax/libs/Base64/0.3.0/base64.min.js')).toBe('0.3.0');
expect(getVersion('https://cdnjs.cloudflare.com/ajax/libs/angular-google-maps/2.1.0-X.10/angular-google-maps.min.js')).toBe('2.1.0-X.10');
expect(getVersion('https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.8-M1/swagger-ui.min.js')).toBe('2.1.8-M1');
expect(getVersion('https://cdnjs.cloudflare.com/BLAH/BLAH.min.js')).toBe(null);
});

Related

Twitter style (stock) mention with Slate.js

I am building a note taking app and for text I use the Slate.js editor. Twitter mention also works with stocks like this
It basically turns into a mention if /\$[a-zA-Z]{1,6}/ is true. What I have tried is to use the normalizeNode callback to change if the regex matches, by deleting the text and inserting a new node at that location but so far I've been unsuccessful.
Slate.js also has a nice set of examples but unfortunately haven't found any of them to demonstrate what I'm trying to do. What would be a good way to go about implementing this feature? Am I wrong to assume it's through using normalizeNode?
I solved this question when working on typed in emojis. For example, when a person typed :smile: we wanted the emoji to appear (a la Slack). The only differences with stock symbols and emojis would be the stock lookup and the usage of Transforms.insertNodes instead of Transforms.insertText.
The code below should be enough to help someone solve their use case. The key is startIndex and endIndex which targets the replacement.
Here's my solution:
editor.normalizeNode = entry => {
const [node, path] = entry;
if (!Text.isText(node)) {
return normalizeNode([node, path]);
}
const emojiMatch = node.text.match(EMOJI_REGEX);
if (!emojiMatch) {
return normalizeNode([node, path]);
}
const [searchMatch, colonMatch] = emojiMatch;
const { index: startIndex } = emojiMatch;
const endIndex = startIndex + searchMatch.length;
const [matchedEmoji] = emojiIndex.search(colonMatch).map(emoji => emoji) as BaseEmoji[];
if (!matchedEmoji) {
return normalizeNode([node, path]);
}
Transforms.insertText(editor, matchedEmoji.native, {
at: {
anchor: { path, offset: startIndex },
focus: { path, offset: endIndex },
}
})
normalizeNode([node, path]);
}

ES2015 initialization of a module not works

my attempts to build a module wrapping a collection fail
i have something like:
// topic: chess gaming
// file: src/core/Refs.js
const COLS = 'abcdefgh'.split('')
const ROWS = '12345678'.split('')
const ALL = new Map()
class Ref {
constructor (col, row) {
this.col = col
this.row = row
this.key = String(col + row)
}
// translate to ref to obtain another
// or null, if off-board
//
// ref.translate(0, 0) === ref (ok ?)
//
translate (dcol, drow) {
// compute something for dcol and drow
// ...
return ALL.get( COLS[colIdx] + ROWS[rowIdx] )
}
}
// the code which seems to not work propertly
for(let c of COLS) {
for(let r of ROWS) {
ALL.set(String(c + r), new Ref(c, r))
}
}
export default {
COLS, ROWS, ALL,
has (ref) {
return ALL.has(ref)
},
get (ref) {
return ALL.get(ref)
},
// trying to grant/warrant "immutability"
// for the "ALL" collection inside the module
keys() {
return [...ALL.keys()]
}
}
After I complie the module with Buble with correct flags (dangerousForOf, ..) and objectAssign
Then I test with karma + jasmine, the first test :
it ('should be 64-length array', function () {
expect (Refs.keys().length).toEqual(64)
})
will faim with 'expects 1 to equal 64', and a log of Refs.ALL seems to show an empty Map
althougth Refs.COLS and Refs.ROWS are properly initialized.
What's happening and how to fix it ?
EDIT:
#Bergi: of course, exposing Refs.ALL breaks the immutability, it was rather for debugging purposes
I'm not exactly sure how to capture test bundle output, but looking at gulp+rollup developpement bundle, the method keys() line :
return [...ALL.keys()]
was replaced by:
return [].concat(ALL.keys())
which produces an 1-element array containing a MapIterator, this breaking tests. putting something like :
return Array.from( ALL.keys() )
will fix the problem, but risks not to work properky in legacy browsers !
I have pushed the code on my repo : https://github.com/hefeust/colorchess-v2
Hoping this could help to fix the bug : how to convert spread (...) operator in source code to have a bundle with the correct Object.assign polyfill ?
Bublé does not support iterators, which is why it transpiles array literals with spread syntax to concatenations. Use Array.from instead (which is more descriptive anyway).
return Array.from( ALL.keys() ) will fix the problem, but risks not to work properly in legacy browsers!
That should be of no concern - you are using Map objects and their keys() method that returns an iterator, which won't work in legacy browsers either. If you plan to support them, you have to use a polyfill anyway - and you just would get a polyfill for Array.from as well.

javascript recast .toSource options ignored

I am trying to transform and reformat this javascript code:
if (name == "c") {b();}
using this recode plugin:
return j(file.source)
.find(j.Identifier)
.forEach(path => {
j(path).replaceWith(
j.identifier(path.node.name.split('').reverse().join(''))
);
})
.toSource({quote:'single'});
as saved here https://astexplorer.net/#/gist/994b660144d9e065906dc41bc14c9c39/c3910178f527d57de5422a0ddce9e515a460182d
I want to get the following output:
if (eman == 'c') {
b();
}
but the {quote:'single'} option is ignored, and I am not sure that there is an option to force indent on if body on new line.
Is this a bug with astexplorer, recode or I am doing something wrong?
The problem is that .toSource() uses recast.print() which tries to retain original formatting. prettyPrint() will respect more options:
var rc = require('recast');
rc.prettyPrint(ast, {quote:'single'}).code

Advanced arrays and loops in Javascript

I am trying to write a complex function that involves arrays. The problem involves an (imaginary) package installer, with each package containing either 0 or 1 dependencies. The task is to order the packages and dependencies in order so that the install is successful.
The function should accept an array of strings defining dependencies. Each string contains the name of a package followed by a colon and space, then any dependencies required by that package. The program should output a comma separated list of package names in the order of install, such that a package’s dependency will always precede that package.
For example, an input of
['KittenService: ','Leetmeme: Cyberportal','Cyberportal: Ice','CamelCaser: KittenService','Fraudstream: Leetmeme','Ice: ']
should output
'KittenService, Ice, Cyberportal, Leetmeme, CamelCaser, Fraudstream'
I've got the basic steps of the function down like reversing the order of the package and dependency and eliminating the colon. However, when it comes to a more complex system like the one above, I am having trouble. Can anyone help me?
The idea is to form a directed acyclic graph (DAG) then perform a topological sorting on the graph. My solution below doesn't really form a proper DAG but it does a topological sorting using depth-first search. It works for your case. However, this won't work for all cases but you can use the two ideas above to create your own perfect version.
var input = [
'KittenService: ',
'Leetmeme: Cyberportal',
'Cyberportal: Ice',
'CamelCaser: KittenService',
'Fraudstream: Leetmeme',
'Ice: '
];
var dependencies = {};
var result = [];
// Form the dependency graph
for (var i = 0; i < input.length; i += 1) {
var inputSplit = input[i].split(':');
var key = inputSplit[0].trim();
var value = inputSplit[1].trim();
dependencies[key] = value;
}
// Depth-first search
for (var key in dependencies) {
if (dependencies.hasOwnProperty(key)) {
visit(key);
}
}
function visit(key) {
if (!dependencies.hasOwnProperty(key)) {
return;
}
if (dependencies[key] !== '') {
visit(dependencies[key]);
}
result.push(key);
delete dependencies[key];
}
console.log(result);
Here is my way to solve it, my idea was to find the dependencies using iterations. Take a look at the comments in the code
function dependencies(inputPackages){
// the final list of packages
var packages = []
// list of packages there having a dependency
var packagesWithDependency = [];
inputPackages.forEach(function(package){
package = package.split(": "); // seperate package and dependency
if(package[1] === ""){ // package has no dependencies, append it directly to list of packages
packages.push(package[0])
}else{ // package has a dependency, save it for later
packagesWithDependency.push({
package: package[0],
dependencie: package[1]
})
}
})
// while there are unresolved packages
while(packagesWithDependency.length > 0){
// we need this to check if there was found a package in this iteration
var packageWithDependencyCount = packagesWithDependency.length;
packagesWithDependency.forEach(function(packageWithDependency, index, object){
// if the dependencies for a package is found in the packages list, then add the new package, and remove it from the packagesWithDependency list
if( packages.indexOf(packageWithDependency.dependencie) >= 0 ){
packages.push(packageWithDependency.package);
object.splice(index, 1);
}
})
// if no package was resolved in this iteration, then return false, the input requires a package there wasn't specified
if(packagesWithDependency.length == packageWithDependencyCount){
return false;
}
}
// return packages // if you want the array instead
return packages.join(", ")
}
console.log(dependencies(['KittenService: ','Leetmeme: Cyberportal','Cyberportal: Ice','CamelCaser: KittenService','Fraudstream: Leetmeme','Ice: ']))
console.log(dependencies(['KittenService: ','Leetmeme: Unknown package']))
This solution can be extended to handle multiple dependencies.
Array.sort can do wonders too, and it makes your code much more concise:
function orderByDependency(input) {
return input.map(function(str, i) {
return {
dependency: str.split(': ')[1] ? str.split(': ')[1] : false,
name: str.split(': ')[0]
};
}).sort(function(a, b) {
return b.dependency === false || a.dependency == b.name;
});
}
document.body.innerHTML = orderByDependency([
'KittenService: ',
'Leetmeme: Cyberportal',
'Cyberportal: Ice',
'CamelCaser: KittenService',
'Fraudstream: Leetmeme',
'Ice: '
]).map(function(pkg) {
return '<div> Loading ' + pkg.name + '...<hr></div>';
}).join('');

How to Combine Multiple Return Functions (JavaScript)

I am learning JavaScript so that I can implement Google Tag Manager. I have a list of paths that I would like GTM to rewrite to something friendlier like so:
function() {
return document.location.pathname.indexOf('/l/138281/2016-06-07/dy383') > -1 ? 'Test Success' : undefined;
}
function() {
return document.location.pathname.indexOf('/l/138281/2016-04-03/55z63') > -1 ? 'SPP Contact Success' : undefined;
I'm just not sure how to combine these returns into one function (I currently have about 30 URLs to rewrite). I imagine I can use if/else, but advice would be quite lovely.
--edit--
URL Path Rewrite To
/test-638-jsj /test-success
/spp-zxcv-765 /spp-contact-success
/foo-asdf-123 /foo
/foo-bar-987 /foo-bar
The return function mentioned above does this beautifully for an individual link. I just want to be able to rewrite a series of URLs in one function (or however it makes sense to do this most specifically). Hopefully that helps clarify.
Thanks!
It is always a great idea to structure your code: separate abstract functionality from the specific problem.
What you are actually doing is scannins strings for occurences of keywords and returning specific values if such a keyword has been found.
Therefore, you need a function performing the above computation and a JavaScript datastructure holding your keywords and their values (= Object):
// Return patterns[key] if any key is found in string, else return string:
function match(string, patterns) {
for (key of Object.keys(patterns)) {
if (string.indexOf(key) > -1) return patterns[key];
}
return string;
}
var patterns = {
'/l/138281/2016-06-07/dy383': 'Test Success',
'/l/138281/2016-04-03/55z63': 'SPP Contact Success'
}
console.log(match('/l/138281/2016-06-07/dy383', patterns)); // "Test Success"
console.log(match('/doesnotexist', patterns)); // "/doesnotexist"
console.log(match(document.location.pathname, patterns));

Categories