Skip to content

Instantly share code, notes, and snippets.

@samgiles
Last active September 30, 2023 02:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samgiles/ee430d501bb6e7639719 to your computer and use it in GitHub Desktop.
Save samgiles/ee430d501bb6e7639719 to your computer and use it in GitHub Desktop.
Polyfill.io Name resolution pseudocode
// Functional Helper: When lambda returns an array, compose concat and map to return a flattened result.
// See https://gist.github.com/samgiles/762ee337dff48623e729 for an example
Array.prototype.flatMap = function(lambda) {
return Array.prototype.concat.apply([], this.map(lambda));
};
// Mock, imagine this is some function that maps a modernizr test name to the polyfill.io nomenclature, if it doesn't map,
// it must return the name as is. Must always be an array.
function getListOfPolyFillsForModernizrTest(name) {
var mock = {
'es5array': [
'Array.prototype.every',
'Array.prototype.filter',
'Array.prototype.forEach',
'Array.prototype.indexOf',
'Array.prototype.lastIndexOf',
'Array.prototype.map',
'Array.prototype.some',
'Array.prototype.reduce',
'Array.prototype.reduceRight',
'Array.isArray'
],
'requestanimationframe': [
'Window.requestAnimationFrame'
],
'queryselector': [
'Document.prototype.querySelector',
'Document.prototype.querySelectorAll'
]
};
return mock[name] || [name];
}
// Mock, imagine this is some function that maps Origami module names to Modernizr test names
// If it does not map, then return the name. (Must always be an array)
function getOrigamiModernizrNames(name) {
var mock = {
'o-gallery': [ 'queryselector', 'es5array' ],
'o-ft-buttons': [ 'requestanimationframe', 'es5array' ]
};
return mock[name] || [name];
}
// An ordered list of name resolution functions. The first is called which transforms any origami module names
// then the second which transforms any modernizr names into polyfill.io names.
var polyfillLookUpServices = [
function origami(name) {
return getOrigamiModernizrNames(name);
},
function modernizr(name) {
return getListOfPolyFillsForModernizrTest(name);
}
];
// Apply the service to each polyfill name returning the transformed names.
function processListOfRequestedPolyfills(polyfills, service) {
// Map each name using the service, and flatten into a single array of names.
return polyfills.flatMap(function(polyfillName) {
// Gets an array of polyfills that match the name
return service(polyfillName);
}).filter(function(polyfill, position, self) {
// Filter out any duplicate polyfills
return self.indexOf(polyfill) == position;
});
}
function getPolyfills(polyfills) {
// Keep track of the currently transformed polyfills.
var transformedPolyfills = polyfills;
// Apply each service to the current list of transformed polyfill names.
polyfillLookUpServices.forEach(function(service) {
transformedPolyfills = processListOfRequestedPolyfills(transformedPolyfills, service);
});
// return the final list of transformed polyfill names
return transformedPolyfills;
}
getPolyfills(['es5array', 'requestanimationframe', 'o-gallery']);
// ["Array.prototype.every", "Array.prototype.filter", "Array.prototype.forEach", "Array.prototype.indexOf", "Array.prototype.lastIndexOf", "Array.prototype.map", "Array.prototype.some", "Array.prototype.reduce", "Array.prototype.reduceRight", "Array.isArray", "Window.requestAnimationFrame", "Document.prototype.querySelector", "Document.prototype.querySelectorAll"]
@samgiles
Copy link
Author

This will actually run and work so I guess it's not really pseudo-code.

Anyway, this gist of it is..
The server has a configurable list of name transformers. By default this includes Modernizr test names, although I've included an 'FT Origami' name mapper as an example. These are simply functions that take some name and map it to 1 or more names. These are combined and passed through each service until by the end you should end up with an array of useful polyfill names.

@triblondon
Copy link

This seems to avoid the need to hard code an alias list into the config json for a polyfill, but it doesn't avoid the need to specify the filter function name in the input, because otherwise the input could be ambiguous. Also, since the mapping between the modernizr name and the list of polyfills is hard coded somewhere (in this example, it's right here) anyway, there seems to be complexity with no benefit.

@samgiles
Copy link
Author

This code implements alias expansion using a sequence of 'expansion' functions. What these functions are depend entirely on the developer setting up the service, each function may or may not transform the name. The functions don't need to be mentioned in the input at all. This means we can add an Origami module look up function without altering the open source code at all - well, we just add a function to an array.

getPolyfills takes a list of names, each name is passed into the first polyfill look up function, if the look up fails, the name is returned and the look up is deferred to the next layer of look up functions. This means, in Origami for example, the look up function can get a list of Modernizr names from the origami.json and return them, the next layer will then map aliases or Modernizr names to the polyfill.io naming convention using the polyfill's config.json.

This is essentially a scalable method of adding new naming conventions/aliases without the headache of adding to each polyfill's configuration.

In the example above the names are mapped as follows:

// 1st Function (Origami):
'es5array'  = ['es5array'];
'requestanimationframe' = ['requestanimationframe'];
'o-gallery' = ['queryselector', 'es5array'];
// Flattened Result:
['es5array', 'requestanimationframe', 'queryselector'];

// 2nd Function (Modernizr (or config.json aliases):
'es5array' = ['Array.prototype.every' ... ];
'requestanimationframe' = ['Window.requestAnimationFrame'];
'queryselector' = ['Document.prototype.querySelector', 'Document.prototype.querySelectorAll'];
// Flattened Result:
 ['Document.prototype.querySelector', 'Document.prototype.querySelectorAll', 'Window.requestAnimationFrame', 'Array.prototype.every' ... ];

The reason this works is that names that can't be resolved are always passed the next layer and the flattened result is passed into the next layer having expanded the Origami names. There is nothing stopping the names being namespaced with 'modernizr:' in this example and I'd still have them hard coded in the polyfill config.json. (There would be no hard coding in the Javascript, these were just mock functions).

@wheresrhys
Copy link

This seems to avoid the need to hard code an alias list into the config json for a polyfill

I think this is a big benefit, partly because the aliasing is a property of e.g. Modernizr, Origami, not an intrinsic property of the polyfill. Also it makes it clearer what the relationships when it's not a one-to-one correspondence between a polyfill and an alias.

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