Skip to content

Commit

Permalink
feat(contexts): adds support for passing a context of either 'window'…
Browse files Browse the repository at this point in the history
…, 'worker', or 'node' as a query parameter

Some polyfills only work in specific environments such as where the 'window' object is available.
Other polyfills work in several environments, but ship different versions for different environments.
This commit adds a new (optional) query parameter, 'context', which can be either 'window', 'worker', or 'node'.
If it isn't given, it defaults to 'window'. When a set of polyfills is requested, those that doesn't support the given
context won't be returned. For example, if you request 'request-animation-frame', and pass in 'worker' as the context,
the polyfill won't be added, even if you also pass in the 'force' option. Fixes #2
  • Loading branch information
wessberg committed Feb 7, 2019
1 parent 5b75228 commit 5266c25
Show file tree
Hide file tree
Showing 10 changed files with 693 additions and 316 deletions.
930 changes: 630 additions & 300 deletions src/constant/constant.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/polyfill/i-polyfill-feature.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {PolyfillDealiasedName} from "./polyfill-name";
import {PolyfillContext} from "./polyfill-context";

// tslint:disable:no-any

Expand All @@ -9,6 +10,7 @@ export interface IPolyfillFeatureMeta {
export interface IPolyfillFeature {
name: PolyfillDealiasedName;
meta: IPolyfillFeatureMeta;
context: PolyfillContext;
}

export interface IPolyfillFeatureInput extends IPolyfillFeature {
Expand Down
16 changes: 16 additions & 0 deletions src/polyfill/polyfill-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {stringTuple} from "../util/type/string-tuple";
import {ElementOf} from "../util/type/element-of";

export const POLYFILL_CONTEXTS = stringTuple("node", "window", "worker");

export type PolyfillContext = ElementOf<typeof POLYFILL_CONTEXTS>;

export const NODE_CONTEXT: Set<PolyfillContext> = new Set(stringTuple("node"));
export const WINDOW_CONTEXT: Set<PolyfillContext> = new Set(stringTuple("window"));
export const WORKER_CONTEXT: Set<PolyfillContext> = new Set(stringTuple("worker"));

export const WINDOW_NODE_CONTEXT: Set<PolyfillContext> = new Set([...WINDOW_CONTEXT, ...NODE_CONTEXT]);

export const WINDOW_WORKER_CONTEXT: Set<PolyfillContext> = new Set([...WINDOW_CONTEXT, ...WORKER_CONTEXT]);

export const ALL_CONTEXTS: Set<PolyfillContext> = new Set([...NODE_CONTEXT, ...WINDOW_WORKER_CONTEXT]);
8 changes: 5 additions & 3 deletions src/polyfill/polyfill-dict.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {PolyfillDealiasedName, PolyfillName} from "./polyfill-name";
import {PolyfillContext} from "./polyfill-context";

export interface IPolyfillDictAlias {
polyfills: PolyfillName[];
Expand All @@ -8,18 +9,19 @@ export interface IPolyfillDictEntryBase {
features: string[];
meta?: {[key: string]: string};
version: string;
contexts: Set<PolyfillContext>;
dependencies: PolyfillName[];
mustComeAfter?: PolyfillName[] | "*";
flatten?: boolean;
}

export interface IPolyfillLibraryDictEntry extends IPolyfillDictEntryBase {
library: string;
relativePaths: string[];
library: string | Record<PolyfillContext, string>;
relativePaths: string[] | Record<PolyfillContext, string[]>;
}

export interface IPolyfillLocalDictEntry extends IPolyfillDictEntryBase {
localPaths: string[];
localPaths: string[] | Record<PolyfillContext, string[]>;
}

export declare type PolyfillDictNormalizedEntry = IPolyfillLibraryDictEntry | IPolyfillLocalDictEntry;
Expand Down
11 changes: 7 additions & 4 deletions src/polyfill/polyfill-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export declare type PolyfillName =
| "dom.collections.iterable"
| "web-components"
| "intl"
| "animation";
| "animation"
| "requestanimationframe"
| "requestidlecallback"
| "setimmediate";

export declare type PolyfillDealiasedName =
| "es.object.assign"
Expand Down Expand Up @@ -294,8 +297,6 @@ export declare type PolyfillDealiasedName =
| "systemjs"
| "performance.now"
| "blob"
| "requestanimationframe"
| "requestidlecallback"
| "url"
| "base64"
| "xhr"
Expand All @@ -315,6 +316,8 @@ export declare type PolyfillDealiasedName =
| "node.parentelement"
| "queryselector"
| "document-fragment"
| "request-idle-callback"
| "request-animation-frame"
| "intersection-observer"
| "mutation-observer"
| "resize-observer"
Expand All @@ -332,5 +335,5 @@ export declare type PolyfillDealiasedName =
| "custom-event"
| "event-source"
| "pointer-event"
| "setimmediate"
| "set-immediate"
| "web-animations";
15 changes: 12 additions & 3 deletions src/service/polyfill-builder/polyfill-builder-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export class PolyfillBuilderService implements IPolyfillBuilderService {
if (!this.isCoreJs(polyfillFeature)) return [];

const match = <IPolyfillLibraryDictEntry>constant.polyfill[polyfillFeature.name];
return match.relativePaths.map(relativePath => relativePath.replace("modules/", "").replace(".js", ""));
const relativePaths = Array.isArray(match.relativePaths) ? match.relativePaths : match.relativePaths[polyfillFeature.context];
return relativePaths.map(relativePath => relativePath.replace("modules/", "").replace(".js", ""));
})
);

Expand Down Expand Up @@ -81,8 +82,16 @@ export class PolyfillBuilderService implements IPolyfillBuilderService {

const flatten = match.flatten === true;

const rootDirectory = "library" in match ? join(__dirname, "../node_modules", match.library) : join(__dirname, "../");
const localPaths = "library" in match ? match.relativePaths : match.localPaths;
const rootDirectory =
"library" in match ? join(__dirname, "../node_modules", typeof match.library === "string" ? match.library : match.library[polyfillFeature.context]) : join(__dirname, "../");
const localPaths =
"library" in match
? Array.isArray(match.relativePaths)
? match.relativePaths
: match.relativePaths[polyfillFeature.context]
: Array.isArray(match.localPaths)
? match.localPaths
: match.localPaths[polyfillFeature.context];

const {meta} = match;

Expand Down
21 changes: 15 additions & 6 deletions src/util/polyfill/polyfill-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {truncate} from "@wessberg/stringutil";
import {IPolyfillLibraryDictEntry, IPolyfillLocalDictEntry} from "../../polyfill/polyfill-dict";
// @ts-ignore
import toposort from "toposort";
import {POLYFILL_CONTEXTS, PolyfillContext} from "../../polyfill/polyfill-context";

// tslint:disable

Expand Down Expand Up @@ -82,11 +83,13 @@ export function getCoreJsBundleIdentifier(paths: string[]): string {
/**
* Returns true if the given polyfill should be included for a particular user agent
* @param {boolean} force
* @param {PolyfillContext} context
* @param {string} userAgent
* @param {string[]} features
* @param {Set<PolyfillContext>} supportedContexts
*/
function shouldIncludePolyfill(force: boolean, userAgent: string, features: string[]): boolean {
return force || features.length < 1 || !userAgentSupportsFeatures(userAgent, ...features);
function shouldIncludePolyfill(force: boolean, context: PolyfillContext, userAgent: string, features: string[], supportedContexts: Set<PolyfillContext>): boolean {
return supportedContexts.has(context) && (force || features.length < 1 || !userAgentSupportsFeatures(userAgent, ...features));
}

/**
Expand Down Expand Up @@ -123,12 +126,12 @@ function getRequiredPolyfillsForUserAgent(polyfillSet: Set<IPolyfillFeatureInput
polyfillSet.forEach(polyfill => {
const match = constant.polyfill[polyfill.name];

if (shouldIncludePolyfill(polyfill.force, userAgent, match.features)) {
if (shouldIncludePolyfill(polyfill.force, polyfill.context, userAgent, match.features, match.contexts)) {
const existingIndex = polyfillNameToPolyfillIndexMap.get(polyfill.name);
if (existingIndex != null) {
polyfills[existingIndex].meta = polyfill.meta;
} else {
polyfills.push({name: polyfill.name, meta: polyfill.meta});
polyfills.push({name: polyfill.name, meta: polyfill.meta, context: polyfill.context});
polyfillNameToPolyfillIndexMap.set(polyfill.name, currentIndex++);
polyfillNames.add(polyfill.name);
}
Expand All @@ -137,7 +140,10 @@ function getRequiredPolyfillsForUserAgent(polyfillSet: Set<IPolyfillFeatureInput
[],
match.dependencies.map(dependency => [...traceAllPolyfillNamesForPolyfillName(dependency)])
);
for (const childPolyfill of getRequiredPolyfillsForUserAgent(new Set(resolvedDependencies.map(dependency => ({name: dependency, meta: {}, force: polyfill.force}))), userAgent)[0]) {
for (const childPolyfill of getRequiredPolyfillsForUserAgent(
new Set(resolvedDependencies.map(dependency => ({name: dependency, meta: {}, force: polyfill.force, context: polyfill.context}))),
userAgent
)[0]) {
if (!polyfillNames.has(childPolyfill.name)) {
polyfills.push({
...childPolyfill,
Expand Down Expand Up @@ -188,6 +194,8 @@ export async function getOrderedPolyfillsWithDependencies(polyfillSet: Set<IPoly
*/
export function getPolyfillRequestFromUrl(url: URL, userAgent: string, encoding?: ContentEncodingKind): IPolyfillRequest {
const featuresRaw = url.searchParams.get("features");
const contextRaw = url.searchParams.get("context") as PolyfillContext;
let context: PolyfillContext = contextRaw == null || !POLYFILL_CONTEXTS.includes(contextRaw) ? "window" : contextRaw;

// Prepare a Set of features
const featureSet: Set<IPolyfillFeatureInput> = new Set();
Expand Down Expand Up @@ -227,7 +235,8 @@ export function getPolyfillRequestFromUrl(url: URL, userAgent: string, encoding?
featureSet.add({
name: polyfillName,
force,
meta
meta,
context
});
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/util/type/deep-partial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Make all properties in T deep-optional
*/
export type DeepPartial<T> = {[P in keyof T]?: DeepPartial<T[P]>};
1 change: 1 addition & 0 deletions src/util/type/element-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type ElementOf<ArrayType> = ArrayType extends (infer ElementType)[] ? ElementType : never;
1 change: 1 addition & 0 deletions src/util/type/string-tuple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const stringTuple = <T extends string[]>(...args: T) => args;

0 comments on commit 5266c25

Please sign in to comment.