From bcf6028bb3e858191bd3c3a76c43416f4cd6fb6f Mon Sep 17 00:00:00 2001 From: wessberg Date: Thu, 7 Mar 2019 11:17:51 +0100 Subject: [PATCH] fix(hosting): fixes a bug in Docker environments --- package.json | 2 +- polyfill-lib/fetch/fetch.js | 526 ++++++++++++++++++ polyfill-lib/object-fit/object-fit-hook.js | 10 + src/constant/constant.ts | 4 +- .../polyfill-builder-service.ts | 3 +- test/server/server.test.ts | 4 +- 6 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 polyfill-lib/fetch/fetch.js create mode 100644 polyfill-lib/object-fit/object-fit-hook.js diff --git a/package.json b/package.json index 805750d..b86a334 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Never worry about polyfills again.", "files": [ "dist/**/*.*", - "src/**/*.*" + "polyfill-lib/**/*.*" ], "scripts": { "serve": "node ./dist/index.js", diff --git a/polyfill-lib/fetch/fetch.js b/polyfill-lib/fetch/fetch.js new file mode 100644 index 0000000..ae69e52 --- /dev/null +++ b/polyfill-lib/fetch/fetch.js @@ -0,0 +1,526 @@ +(function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : factory((global.WHATWGFetch = {})); +})(this, function(exports) { + "use strict"; + + var support = { + searchParams: "URLSearchParams" in self, + iterable: "Symbol" in self && "iterator" in Symbol, + blob: + "FileReader" in self && + "Blob" in self && + (function() { + try { + new Blob(); + return true; + } catch (e) { + return false; + } + })(), + formData: "FormData" in self, + arrayBuffer: "ArrayBuffer" in self + }; + + function isDataView(obj) { + return obj && DataView.prototype.isPrototypeOf(obj); + } + + if (support.arrayBuffer) { + var viewClasses = [ + "[object Int8Array]", + "[object Uint8Array]", + "[object Uint8ClampedArray]", + "[object Int16Array]", + "[object Uint16Array]", + "[object Int32Array]", + "[object Uint32Array]", + "[object Float32Array]", + "[object Float64Array]" + ]; + + var isArrayBufferView = + ArrayBuffer.isView || + function(obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; + }; + } + + function normalizeName(name) { + if (typeof name !== "string") { + name = String(name); + } + if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) { + throw new TypeError("Invalid character in header field name"); + } + return name.toLowerCase(); + } + + function normalizeValue(value) { + if (typeof value !== "string") { + value = String(value); + } + return value; + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function() { + var value = items.shift(); + return {done: value === undefined, value: value}; + } + }; + + if (support.iterable) { + iterator[Symbol.iterator] = function() { + return iterator; + }; + } + + return iterator; + } + + function Headers(headers) { + this.map = {}; + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value); + }, this); + } else if (Array.isArray(headers)) { + headers.forEach(function(header) { + this.append(header[0], header[1]); + }, this); + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]); + }, this); + } + } + + Headers.prototype.append = function(name, value) { + name = normalizeName(name); + value = normalizeValue(value); + var oldValue = this.map[name]; + this.map[name] = oldValue ? oldValue + ", " + value : value; + }; + + Headers.prototype["delete"] = function(name) { + delete this.map[normalizeName(name)]; + }; + + Headers.prototype.get = function(name) { + name = normalizeName(name); + return this.has(name) ? this.map[name] : null; + }; + + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)); + }; + + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = normalizeValue(value); + }; + + Headers.prototype.forEach = function(callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this); + } + } + }; + + Headers.prototype.keys = function() { + var items = []; + this.forEach(function(value, name) { + items.push(name); + }); + return iteratorFor(items); + }; + + Headers.prototype.values = function() { + var items = []; + this.forEach(function(value) { + items.push(value); + }); + return iteratorFor(items); + }; + + Headers.prototype.entries = function() { + var items = []; + this.forEach(function(value, name) { + items.push([name, value]); + }); + return iteratorFor(items); + }; + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries; + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError("Already read")); + } + body.bodyUsed = true; + } + + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result); + }; + reader.onerror = function() { + reject(reader.error); + }; + }); + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsArrayBuffer(blob); + return promise; + } + + function readBlobAsText(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsText(blob); + return promise; + } + + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf); + var chars = new Array(view.length); + + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]); + } + return chars.join(""); + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0); + } else { + var view = new Uint8Array(buf.byteLength); + view.set(new Uint8Array(buf)); + return view.buffer; + } + } + + function Body() { + this.bodyUsed = false; + + this._initBody = function(body) { + this._bodyInit = body; + if (!body) { + this._bodyText = ""; + } else if (typeof body === "string") { + this._bodyText = body; + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body; + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body; + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString(); + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer); + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]); + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body); + } else { + this._bodyText = body = Object.prototype.toString.call(body); + } + + if (!this.headers.get("content-type")) { + if (typeof body === "string") { + this.headers.set("content-type", "text/plain;charset=UTF-8"); + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set("content-type", this._bodyBlob.type); + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8"); + } + } + }; + + if (support.blob) { + this.blob = function() { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])); + } else if (this._bodyFormData) { + throw new Error("could not read FormData body as blob"); + } else { + return Promise.resolve(new Blob([this._bodyText])); + } + }; + + this.arrayBuffer = function() { + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer); + } else { + return this.blob().then(readBlobAsArrayBuffer); + } + }; + } + + this.text = function() { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)); + } else if (this._bodyFormData) { + throw new Error("could not read FormData body as text"); + } else { + return Promise.resolve(this._bodyText); + } + }; + + if (support.formData) { + this.formData = function() { + return this.text().then(decode); + }; + } + + this.json = function() { + return this.text().then(JSON.parse); + }; + + return this; + } + + // HTTP methods whose capitalization should be normalized + var methods = ["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"]; + + function normalizeMethod(method) { + var upcased = method.toUpperCase(); + return methods.indexOf(upcased) > -1 ? upcased : method; + } + + function Request(input, options) { + options = options || {}; + var body = options.body; + + if (input instanceof Request) { + if (input.bodyUsed) { + throw new TypeError("Already read"); + } + this.url = input.url; + this.credentials = input.credentials; + if (!options.headers) { + this.headers = new Headers(input.headers); + } + this.method = input.method; + this.mode = input.mode; + this.signal = input.signal; + if (!body && input._bodyInit != null) { + body = input._bodyInit; + input.bodyUsed = true; + } + } else { + this.url = String(input); + } + + this.credentials = options.credentials || this.credentials || "same-origin"; + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers); + } + this.method = normalizeMethod(options.method || this.method || "GET"); + this.mode = options.mode || this.mode || null; + this.signal = options.signal || this.signal; + this.referrer = null; + + if ((this.method === "GET" || this.method === "HEAD") && body) { + throw new TypeError("Body not allowed for GET or HEAD requests"); + } + this._initBody(body); + } + + Request.prototype.clone = function() { + return new Request(this, {body: this._bodyInit}); + }; + + function decode(body) { + var form = new FormData(); + body + .trim() + .split("&") + .forEach(function(bytes) { + if (bytes) { + var split = bytes.split("="); + var name = split.shift().replace(/\+/g, " "); + var value = split.join("=").replace(/\+/g, " "); + form.append(decodeURIComponent(name), decodeURIComponent(value)); + } + }); + return form; + } + + function parseHeaders(rawHeaders) { + var headers = new Headers(); + // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space + // https://tools.ietf.org/html/rfc7230#section-3.2 + var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " "); + preProcessedHeaders.split(/\r?\n/).forEach(function(line) { + var parts = line.split(":"); + var key = parts.shift().trim(); + if (key) { + var value = parts.join(":").trim(); + headers.append(key, value); + } + }); + return headers; + } + + Body.call(Request.prototype); + + function Response(bodyInit, options) { + if (!options) { + options = {}; + } + + this.type = "default"; + this.status = options.status === undefined ? 200 : options.status; + this.ok = this.status >= 200 && this.status < 300; + this.statusText = "statusText" in options ? options.statusText : "OK"; + this.headers = new Headers(options.headers); + this.url = options.url || ""; + this._initBody(bodyInit); + } + + Body.call(Response.prototype); + + Response.prototype.clone = function() { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }); + }; + + Response.error = function() { + var response = new Response(null, {status: 0, statusText: ""}); + response.type = "error"; + return response; + }; + + var redirectStatuses = [301, 302, 303, 307, 308]; + + Response.redirect = function(url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError("Invalid status code"); + } + + return new Response(null, {status: status, headers: {location: url}}); + }; + + exports.DOMException = self.DOMException; + try { + new exports.DOMException(); + } catch (err) { + exports.DOMException = function(message, name) { + this.message = message; + this.name = name; + var error = Error(message); + this.stack = error.stack; + }; + exports.DOMException.prototype = Object.create(Error.prototype); + exports.DOMException.prototype.constructor = exports.DOMException; + } + + function fetch(input, init) { + return new Promise(function(resolve, reject) { + var request = new Request(input, init); + + if (request.signal && request.signal.aborted) { + return reject(new exports.DOMException("Aborted", "AbortError")); + } + + var xhr = new XMLHttpRequest(); + + function abortXhr() { + xhr.abort(); + } + + xhr.onload = function() { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || "") + }; + options.url = "responseURL" in xhr ? xhr.responseURL : options.headers.get("X-Request-URL"); + var body = "response" in xhr ? xhr.response : xhr.responseText; + resolve(new Response(body, options)); + }; + + xhr.onerror = function() { + reject(new TypeError("Network request failed")); + }; + + xhr.ontimeout = function() { + reject(new TypeError("Network request failed")); + }; + + xhr.onabort = function() { + reject(new exports.DOMException("Aborted", "AbortError")); + }; + + xhr.open(request.method, request.url, true); + + if (request.credentials === "include") { + xhr.withCredentials = true; + } else if (request.credentials === "omit") { + xhr.withCredentials = false; + } + + if ("responseType" in xhr && support.blob) { + xhr.responseType = "blob"; + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value); + }); + + if (request.signal) { + request.signal.addEventListener("abort", abortXhr); + + xhr.onreadystatechange = function() { + // DONE (success or failure) + if (xhr.readyState === 4) { + request.signal.removeEventListener("abort", abortXhr); + } + }; + } + + xhr.send(typeof request._bodyInit === "undefined" ? null : request._bodyInit); + }); + } + + fetch.polyfill = true; + self.fetch = fetch; + self.Headers = Headers; + self.Request = Request; + self.Response = Response; + + exports.Headers = Headers; + exports.Request = Request; + exports.Response = Response; + exports.fetch = fetch; + + Object.defineProperty(exports, "__esModule", {value: true}); +}); diff --git a/polyfill-lib/object-fit/object-fit-hook.js b/polyfill-lib/object-fit/object-fit-hook.js new file mode 100644 index 0000000..5ba78d5 --- /dev/null +++ b/polyfill-lib/object-fit/object-fit-hook.js @@ -0,0 +1,10 @@ +var hasLoaded = document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive"; +if (hasLoaded) objectFitImages(null, {watchMQ: true}); +else + document.addEventListener( + "DOMContentLoaded", + function() { + objectFitImages(null, {watchMQ: true}); + }, + false + ); diff --git a/src/constant/constant.ts b/src/constant/constant.ts index 0566e34..a70caa2 100644 --- a/src/constant/constant.ts +++ b/src/constant/constant.ts @@ -82,7 +82,7 @@ export const constant: IConstant = { contexts: ALL_CONTEXTS }, "object-fit": { - localPaths: ["node_modules/object-fit-images/dist/ofi.min.js", "src/polyfill/lib/object-fit/object-fit-hook.js"], + localPaths: ["node_modules/object-fit-images/dist/ofi.min.js", "polyfill-lib/object-fit/object-fit-hook.js"], features: ["object-fit"], version: environment.NPM_PACKAGE_DEPENDENCIES_OBJECT_FIT_IMAGES, dependencies: ["get-computed-style", "es.object.define-property"], @@ -2690,7 +2690,7 @@ export const constant: IConstant = { contexts: WINDOW_CONTEXT }, fetch: { - localPaths: ["src/polyfill/lib/fetch/fetch.js"], + localPaths: ["polyfill-lib/fetch/fetch.js"], features: ["fetch"], version: environment.NPM_PACKAGE_DEPENDENCIES_WHATWG_FETCH, dependencies: ["es.array.for-each", "es.object.get-own-property-names", "es.promise", "xhr"], diff --git a/src/service/polyfill-builder/polyfill-builder-service.ts b/src/service/polyfill-builder/polyfill-builder-service.ts index 685204e..e3e9f6a 100644 --- a/src/service/polyfill-builder/polyfill-builder-service.ts +++ b/src/service/polyfill-builder/polyfill-builder-service.ts @@ -83,7 +83,8 @@ export class PolyfillBuilderService implements IPolyfillBuilderService { const flatten = match.flatten === true; - const rootDirectory = "library" in match ? sync(join("node_modules", typeof match.library === "string" ? match.library : match.library[polyfillFeature.context]))! : join(sync("src")!, "../"); + const rootDirectory = + "library" in match ? sync(join("node_modules", typeof match.library === "string" ? match.library : match.library[polyfillFeature.context]))! : join(sync("polyfill-lib")!, "../"); const localPaths = "library" in match diff --git a/test/server/server.test.ts b/test/server/server.test.ts index 4cfddf3..376a199 100644 --- a/test/server/server.test.ts +++ b/test/server/server.test.ts @@ -69,7 +69,9 @@ test("Will generate correct polyfills for IE11", async t => { method: "GET", host: config.host, port: config.port, - path: `${constant.endpoint.polyfill}?features=event,custom-event,zone,es.promise.finally,pointer-event|force,systemjs|variant=system`, + path: `${ + constant.endpoint.polyfill + }?features=web-components,es,class-list,system|variant=system,custom-event,url,fetch,object-fit,intersection-observer,animation,regenerator-runtime,requestanimationframe,requestidlecallback,resize-observer,pointer-event,dom.collections.iterable,scroll-behavior,zone|error,esnext.reflect`, acceptEncoding: undefined });