mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
feat(polyfills-loader): expose create-polyfills-data
This commit also: - introduces ordering when defining polyfills - allows explicit inclusion of systemjs polyfill - makes files optional when defining configs
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
const { injectPolyfillsLoader } = require('./src/inject-polyfills-loader');
|
||||
const { createPolyfillsLoader } = require('./src/create-polyfills-loader');
|
||||
const { createPolyfillsData } = require('./src/create-polyfills-data');
|
||||
const {
|
||||
noModuleSupportTest,
|
||||
fileTypes,
|
||||
@@ -24,6 +25,7 @@ module.exports = {
|
||||
injectPolyfillsLoader,
|
||||
createPolyfillsLoader,
|
||||
|
||||
createPolyfillsData,
|
||||
noModuleSupportTest,
|
||||
fileTypes,
|
||||
createContentHash,
|
||||
|
||||
@@ -9,7 +9,7 @@ const { createContentHash, noModuleSupportTest, hasFileOfType, fileTypes } = req
|
||||
|
||||
/**
|
||||
* @param {PolyfillsLoaderConfig} cfg
|
||||
* @returns {{ coreJs?: PolyfillFile, polyfillFiles: PolyfillFile[] }}
|
||||
* @returns {PolyfillFile[]}
|
||||
*/
|
||||
function createPolyfillsData(cfg) {
|
||||
const { polyfills = {} } = cfg;
|
||||
@@ -76,9 +76,14 @@ function createPolyfillsData(cfg) {
|
||||
// load systemjs, an es module polyfill, if one of the entries needs it
|
||||
const hasSystemJs =
|
||||
cfg.polyfills && cfg.polyfills.custom && cfg.polyfills.custom.find(c => c.name === 'systemjs');
|
||||
if (!hasSystemJs && hasFileOfType(cfg, fileTypes.SYSTEMJS)) {
|
||||
if (
|
||||
polyfills.systemjs ||
|
||||
polyfills.systemjsExtended ||
|
||||
(!hasSystemJs && hasFileOfType(cfg, fileTypes.SYSTEMJS))
|
||||
) {
|
||||
const name = 'systemjs';
|
||||
const alwaysLoad = cfg.modern.files.some(f => f.type === fileTypes.SYSTEMJS);
|
||||
const alwaysLoad =
|
||||
cfg.modern && cfg.modern.files && cfg.modern.files.some(f => f.type === fileTypes.SYSTEMJS);
|
||||
const test = alwaysLoad || !cfg.legacy ? undefined : cfg.legacy.map(e => e.test).join(' || ');
|
||||
|
||||
if (polyfills.systemjsExtended) {
|
||||
@@ -177,6 +182,8 @@ function createPolyfillsData(cfg) {
|
||||
});
|
||||
}
|
||||
|
||||
polyfillConfigs.sort((p1, p2) => (p1.order || 50) - (p2.order || 50));
|
||||
|
||||
/**
|
||||
* @description returns the contents of a file at the given path
|
||||
* @param {string} filePath
|
||||
@@ -192,8 +199,6 @@ function createPolyfillsData(cfg) {
|
||||
|
||||
/** @type {PolyfillFile[]} */
|
||||
const polyfillFiles = [];
|
||||
/** @type {PolyfillFile | undefined} */
|
||||
let coreJs;
|
||||
|
||||
for (const polyfillConfig of polyfillConfigs) {
|
||||
if (!polyfillConfig.name || !polyfillConfig.path) {
|
||||
@@ -217,6 +222,7 @@ function createPolyfillsData(cfg) {
|
||||
)}.js`;
|
||||
|
||||
const polyfillFile = {
|
||||
name: polyfillConfig.name,
|
||||
type: polyfillConfig.fileType || fileTypes.SCRIPT,
|
||||
path: filePath,
|
||||
content,
|
||||
@@ -224,14 +230,10 @@ function createPolyfillsData(cfg) {
|
||||
initializer: polyfillConfig.initializer,
|
||||
};
|
||||
|
||||
if (polyfillConfig.name !== 'core-js') {
|
||||
polyfillFiles.push(polyfillFile);
|
||||
} else {
|
||||
coreJs = polyfillFile;
|
||||
}
|
||||
polyfillFiles.push(polyfillFile);
|
||||
}
|
||||
|
||||
return { coreJs, polyfillFiles };
|
||||
return polyfillFiles;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -102,7 +102,7 @@ function createLoadFiles(files) {
|
||||
* @param {PolyfillsLoaderConfig} cfg
|
||||
*/
|
||||
function createLoadFilesFunction(cfg) {
|
||||
const loadResources = createLoadFiles(cfg.modern.files);
|
||||
const loadResources = cfg.modern && cfg.modern.files ? createLoadFiles(cfg.modern.files) : '';
|
||||
if (!cfg.legacy || cfg.legacy.length === 0) {
|
||||
return loadResources;
|
||||
}
|
||||
@@ -197,7 +197,10 @@ function createPolyfillsLoaderCode(cfg, polyfills) {
|
||||
* @returns {PolyfillsLoader | null}
|
||||
*/
|
||||
function createPolyfillsLoader(cfg) {
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(cfg);
|
||||
let polyfillFiles = createPolyfillsData(cfg);
|
||||
/** @type {PolyfillFile | undefined} */
|
||||
const coreJs = polyfillFiles.find(pf => pf.name === 'core-js');
|
||||
polyfillFiles = polyfillFiles.filter(pf => pf !== coreJs);
|
||||
const { loadPolyfillsCode, generatedFiles } = createPolyfillsLoaderCode(cfg, polyfillFiles);
|
||||
|
||||
let code = `
|
||||
|
||||
8
packages/polyfills-loader/src/types.d.ts
vendored
8
packages/polyfills-loader/src/types.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
export interface PolyfillsLoaderConfig {
|
||||
// files to load on modern browsers. loaded when there are no
|
||||
// legacy entrypoints which match
|
||||
modern: ModernEntrypoint;
|
||||
modern?: ModernEntrypoint;
|
||||
// legacy entrypoints to load on older browsers, each entrypoint
|
||||
// consists of a test when to load it. tests are executed in the order
|
||||
// that they appear using if else statement where the final else
|
||||
@@ -36,6 +36,8 @@ export interface PolyfillsConfig {
|
||||
intersectionObserver?: boolean;
|
||||
resizeObserver?: boolean;
|
||||
dynamicImport?: boolean;
|
||||
// systemjs s.js
|
||||
systemjs?: boolean;
|
||||
// systemjs extended version with import maps
|
||||
systemjsExtended?: boolean;
|
||||
esModuleShims?: boolean;
|
||||
@@ -56,6 +58,8 @@ export interface PolyfillConfig {
|
||||
minify?: boolean;
|
||||
// code used to initialze the module
|
||||
initializer?: string;
|
||||
// order of the polyfill when loaded, defaults to 50
|
||||
order?: number;
|
||||
}
|
||||
|
||||
export interface ModernEntrypoint {
|
||||
@@ -85,6 +89,8 @@ export interface GeneratedFile extends File {
|
||||
}
|
||||
|
||||
export interface PolyfillFile extends GeneratedFile {
|
||||
// name of the polyfill
|
||||
name: string;
|
||||
// runtime feature detection to load this polyfill
|
||||
test?: string;
|
||||
// code run after the polyfill is loaded to initialize the polyfill
|
||||
|
||||
@@ -41,25 +41,27 @@ describe('polyfills', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
});
|
||||
|
||||
expect(coreJs).to.eql({
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/core-js.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype)",
|
||||
});
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'core-js',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/core-js.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype)",
|
||||
},
|
||||
{
|
||||
name: 'fetch',
|
||||
path: 'polyfills/fetch.js',
|
||||
test: "!('fetch' in window)",
|
||||
type: 'script',
|
||||
},
|
||||
{
|
||||
name: 'dynamic-import',
|
||||
initializer:
|
||||
"window.dynamicImportPolyfill.initialize({ importFunctionName: 'importShim' });",
|
||||
path: 'polyfills/dynamic-import.js',
|
||||
@@ -68,28 +70,33 @@ describe('polyfills', () => {
|
||||
type: 'script',
|
||||
},
|
||||
{
|
||||
name: 'es-module-shims',
|
||||
path: 'polyfills/es-module-shims.js',
|
||||
test: "'noModule' in HTMLScriptElement.prototype",
|
||||
type: 'module',
|
||||
},
|
||||
{
|
||||
name: 'intersection-observer',
|
||||
path: 'polyfills/intersection-observer.js',
|
||||
test:
|
||||
"!('IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype)",
|
||||
type: 'script',
|
||||
},
|
||||
{
|
||||
name: 'resize-observer',
|
||||
path: 'polyfills/resize-observer.js',
|
||||
test: "!('ResizeObserver' in window)",
|
||||
type: 'script',
|
||||
},
|
||||
{
|
||||
name: 'webcomponents',
|
||||
path: 'polyfills/webcomponents.js',
|
||||
test:
|
||||
"!('attachShadow' in Element.prototype) || !('getRootNode' in Element.prototype) || (window.ShadyDOM && window.ShadyDOM.force)",
|
||||
type: 'script',
|
||||
},
|
||||
{
|
||||
name: 'custom-elements-es5-adapter',
|
||||
path: 'polyfills/custom-elements-es5-adapter.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype) && 'getRootNode' in Element.prototype",
|
||||
type: 'script',
|
||||
@@ -107,14 +114,14 @@ describe('polyfills', () => {
|
||||
abortController: true,
|
||||
},
|
||||
};
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
});
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'fetch',
|
||||
path: 'polyfills/fetch.js',
|
||||
test:
|
||||
"!('fetch' in window) || !('Request' in window) || !('signal' in window.Request.prototype)",
|
||||
@@ -133,14 +140,14 @@ describe('polyfills', () => {
|
||||
shadyCssCustomStyle: true,
|
||||
},
|
||||
};
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
});
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'webcomponents-shady-css-custom-style',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/webcomponents-shady-css-custom-style.js',
|
||||
test: "!('attachShadow' in Element.prototype) || !('getRootNode' in Element.prototype)",
|
||||
@@ -163,7 +170,7 @@ describe('polyfills', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { polyfillFiles } = createPolyfillsData(config);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
@@ -171,6 +178,7 @@ describe('polyfills', () => {
|
||||
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'systemjs',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/systemjs.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype)",
|
||||
@@ -197,8 +205,7 @@ describe('polyfills', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
@@ -206,6 +213,7 @@ describe('polyfills', () => {
|
||||
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'systemjs',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/systemjs.js',
|
||||
test: "'foo' in bar || !('noModule' in HTMLScriptElement.prototype)",
|
||||
@@ -222,8 +230,7 @@ describe('polyfills', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
@@ -231,6 +238,7 @@ describe('polyfills', () => {
|
||||
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'systemjs',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/systemjs.js',
|
||||
},
|
||||
@@ -243,10 +251,12 @@ describe('polyfills', () => {
|
||||
name: 'polyfill-a',
|
||||
test: "'foo' in window",
|
||||
path: path.resolve(__dirname, 'custom-polyfills/polyfill-a.js'),
|
||||
order: 30,
|
||||
},
|
||||
{
|
||||
name: 'polyfill-b',
|
||||
path: path.resolve(__dirname, 'custom-polyfills/polyfill-b.js'),
|
||||
order: 60,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -263,28 +273,54 @@ describe('polyfills', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { coreJs, polyfillFiles } = createPolyfillsData(config);
|
||||
cleanupPolyfill(coreJs);
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
});
|
||||
|
||||
expect(coreJs).to.eql({
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/core-js.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype)",
|
||||
});
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'polyfill-a',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/polyfill-a.js',
|
||||
test: "'foo' in window",
|
||||
},
|
||||
{
|
||||
name: 'core-js',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/core-js.js',
|
||||
test: "!('noModule' in HTMLScriptElement.prototype)",
|
||||
},
|
||||
{
|
||||
name: 'polyfill-b',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/polyfill-b.js',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('loads systemjs separatly if requested', () => {
|
||||
/** @type {PolyfillsLoaderConfig} */
|
||||
const config = {
|
||||
polyfills: {
|
||||
hash: false,
|
||||
systemjs: true,
|
||||
},
|
||||
};
|
||||
|
||||
const polyfillFiles = createPolyfillsData(config);
|
||||
polyfillFiles.forEach(p => {
|
||||
expect(p.content).to.be.a('string');
|
||||
cleanupPolyfill(p);
|
||||
});
|
||||
|
||||
expect(polyfillFiles).to.eql([
|
||||
{
|
||||
name: 'systemjs',
|
||||
type: fileTypes.SCRIPT,
|
||||
path: 'polyfills/systemjs.js',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user