From 6bd7e10c4dceea204be634a74d17de1fc584d7aa Mon Sep 17 00:00:00 2001
From: TheEmeraldStarr <46467239+Epicloudygamer@users.noreply.github.com>
Date: Sun, 8 Aug 2021 14:55:47 -0700
Subject: [PATCH] Minor Change (oops)
---
app.js | 2 +-
src/Corrosion/README.md | 223 +
src/Corrosion/demo/index.html | 16 +
src/Corrosion/demo/index.js | 19 +
src/Corrosion/demo/ssl.cert | 22 +
src/Corrosion/demo/ssl.key | 28 +
src/Corrosion/lib/browser/document.js | 271 +
src/Corrosion/lib/browser/history.js | 26 +
src/Corrosion/lib/browser/http.js | 92 +
src/Corrosion/lib/browser/index.js | 195 +
src/Corrosion/lib/browser/location.js | 56 +
src/Corrosion/lib/browser/worker.js | 33 +
src/Corrosion/lib/codec.js | 66 +
src/Corrosion/lib/cookie-parser.js | 95 +
src/Corrosion/lib/cookie.js | 34 +
src/Corrosion/lib/css.js | 67 +
src/Corrosion/lib/esotope.js | 2562 ++
src/Corrosion/lib/html.js | 226 +
src/Corrosion/lib/js.js | 185 +
src/Corrosion/lib/rewrite.js | 27 +
src/Corrosion/lib/server/bundle.js | 30109 +++++++++++++++++++++
src/Corrosion/lib/server/decompress.js | 22 +
src/Corrosion/lib/server/gateway.js | 21 +
src/Corrosion/lib/server/headers.js | 45 +
src/Corrosion/lib/server/index.js | 60 +
src/Corrosion/lib/server/middleware.js | 14 +
src/Corrosion/lib/server/request.js | 83 +
src/Corrosion/lib/server/rewrite-body.js | 36 +
src/Corrosion/lib/server/upgrade.js | 56 +
src/Corrosion/lib/url.js | 34 +
src/Corrosion/package.json | 47 +
31 files changed, 34771 insertions(+), 1 deletion(-)
create mode 100644 src/Corrosion/README.md
create mode 100644 src/Corrosion/demo/index.html
create mode 100644 src/Corrosion/demo/index.js
create mode 100644 src/Corrosion/demo/ssl.cert
create mode 100644 src/Corrosion/demo/ssl.key
create mode 100644 src/Corrosion/lib/browser/document.js
create mode 100644 src/Corrosion/lib/browser/history.js
create mode 100644 src/Corrosion/lib/browser/http.js
create mode 100644 src/Corrosion/lib/browser/index.js
create mode 100644 src/Corrosion/lib/browser/location.js
create mode 100644 src/Corrosion/lib/browser/worker.js
create mode 100644 src/Corrosion/lib/codec.js
create mode 100644 src/Corrosion/lib/cookie-parser.js
create mode 100644 src/Corrosion/lib/cookie.js
create mode 100644 src/Corrosion/lib/css.js
create mode 100644 src/Corrosion/lib/esotope.js
create mode 100644 src/Corrosion/lib/html.js
create mode 100644 src/Corrosion/lib/js.js
create mode 100644 src/Corrosion/lib/rewrite.js
create mode 100644 src/Corrosion/lib/server/bundle.js
create mode 100644 src/Corrosion/lib/server/decompress.js
create mode 100644 src/Corrosion/lib/server/gateway.js
create mode 100644 src/Corrosion/lib/server/headers.js
create mode 100644 src/Corrosion/lib/server/index.js
create mode 100644 src/Corrosion/lib/server/middleware.js
create mode 100644 src/Corrosion/lib/server/request.js
create mode 100644 src/Corrosion/lib/server/rewrite-body.js
create mode 100644 src/Corrosion/lib/server/upgrade.js
create mode 100644 src/Corrosion/lib/url.js
create mode 100644 src/Corrosion/package.json
diff --git a/app.js b/app.js
index c0a45436..f7fe9832 100644
--- a/app.js
+++ b/app.js
@@ -4,7 +4,7 @@
* MIT license: http://opensource.org/licenses/MIT
* ----------------------------------------------- */
const
- corrosion = require('corrosion'),
+ corrosion = require('./src/Corrosion'),
path = require('path'),
config = require('./config.json'),
fs = require('fs'),
diff --git a/src/Corrosion/README.md b/src/Corrosion/README.md
new file mode 100644
index 00000000..4df0221c
--- /dev/null
+++ b/src/Corrosion/README.md
@@ -0,0 +1,223 @@
+# Corrosion
+Titanium Networks main web proxy.
+Successor to [Alloy](https://github.com/titaniumnetwork-dev/alloy)
+# Installation:
+```
+npm i corrosion
+```
+
+# Example:
+```javascript
+const Corrosion = require('corrosion');
+const proxy = new Corrosion();
+const http = require('http')
+http.createServer((req, res) =>
+ proxy.request(req, res) // Request Proxy
+).on('upgrade', (req, socket, head) =>
+ proxy.upgrade(req, socket, head) // WebSocket Proxy
+).listen(80);
+```
+Much more in depth one is in the [demo folder](demo/).
+
+# API:
+
+
+## Index
+- `config`
+ - `prefix` String - URL Prefix
+ - `title` (Boolean / String) - Title used for HTML documents
+ - `ws` Boolean - WebSocket rewriting
+ - `cookie` Boolean - Request Cookies
+ - `codec` String - URL encoding (base64, plain, xor).
+ - `requestMiddleware` Array - Array of [middleware](#middleware) functions for proxy request (Server).
+ - `responseMiddleware` Array - Array of [middleware](#middleware) functions for proxy response (Server).
+ - `standardMiddleware` Boolean - Use the prebuilt [middleware](#middleware) used by default (Server).
+
+#### request
+ - `request` Request
+ - `response` Response
+
+#### upgrade
+ - `request` Request
+ - `socket` Socket
+ - `head` Head
+
+#### bundleScripts
+Bundles scripts for client injection. Important when updating proxy.
+
+## Properties
+ - [url](#url)
+ - [html](#html)
+ - [js](#js)
+ - [css](#css)
+ - [cookies](#cookies)
+ - [config](#index)
+ - [codec](#codec)
+ - [prefix](#url)
+
+
+
+## url
+
+#### wrap
+ - `val` String
+ - `config` Configuration
+ - `base` WHATWG URL
+ - `origin` Location origin - Adds a location origin before the proxy url
+ - `flags` Array - ['xhr'] => /service/xhr_/https%3A%2F%2Fexample.org/
+
+#### unwrap
+ - `val` String
+ - `config` Configuration
+ - `origin` Location origin - Required if a location origin starts before the proxy url
+ - `flags` Boolean - Returns with both the URL and flags found { value: 'https://example.org', flags: ['xhr'], })
+ - `leftovers` Boolean - Use any leftovers if any after the encoded proxy url
+
+
+## Properties
+ - `regex` Regex used to determine to rewrite the URL or not.
+
+ - `prefix` URL Prefix
+
+ - `codec` (base64, plain, xor)
+
+
+## js
+
+#### process
+ - `source` JS script
+ - `url` URL for heading
+
+#### iterate
+ - `ast` JS AST
+ - `Callback` Handler initated on AST node
+
+#### createHead
+ - `url` URL for heading
+
+#### createCallExperssion
+ - `callee` Acorn.js Node
+ - `args` Array
+
+#### createArrayExpression
+ - `elements` Array
+
+#### createIdentifier
+ - `name` Identifier name
+ - `preventRewrite` Prevent further rewrites
+
+#### createLiteral
+ - `value` Literal value
+
+## css
+
+#### process
+ - `source` CSS
+ - `config` Configuration
+ - `base` WHATWG URL
+ - `origin` Location origin
+ - `context` CSS-Tree context
+
+## html
+
+#### process
+ - `source` HTML Source
+ - `config` Configuration
+ - `document` Determines of its a document or fragment for parsing
+ - `base` WHATWG URL
+ - `origin` Location origin
+
+#### source
+ - `processed` Rewritten HTML
+ - `config` Configuration
+ - `document` Determines of its a document or fragment for parsing
+
+### Properties
+- `map` Map for attribute rewriting
+
+
+## cookies
+
+#### encode
+ - `input` New (Cookie / Cookies)
+ - `config` Configuration
+ - `url` WHATWG URL
+ - `domain` Cookie Domain
+ - `secure` Cookie Secure
+
+#### decode
+ - `store` Encoded Cookies
+ - `config` Configuration
+ - `url` WHATWG URL
+
+## codec
+
+#### encode
+#### decode
+ - `str` String
+
+## middleware
+
+Middleware are functions that will be executed either before request or after response. These can alter the way a request is made or response is sent.
+
+```javascript
+function(ctx) {r
+ ctx.body; // (Request / Response) Body (Will return null if none)
+ ctx.headers; // (Request / Response) Headers
+ ctx.url; // WHATWG URL
+ ctx.flags; // URL Flags
+ ctx.origin; // Request origin
+ ctx.method; // Request method
+ ctx.rewrite; // Corrosion object
+ ctx.statusCode; // Response status (Only available on response)
+ ctx.agent; // HTTP agent
+ ctx.address; // Address used to make remote request
+ ctx.clientSocket; // Node.js Server Socket (Only available on upgrade)
+ ctx.clientRequest; // Node.js Server Request
+ ctx.clientResponse; // Node.js Server Response
+ ctx.remoteResponse; // Node.js Remote Response (Only available on response)
+};
+```
+
+### Default middleware
+
+- Request
+ - requestHeaders
+
+- Response
+ - responseHeaders
+ - decompress
+ - rewriteBody
+
+### Available Middleware
+
+#### address (Request)
+ - `arr` Array of IP addresses to use in request
+
+```javascript
+const Corrosion = require('corrosion');
+const proxy = new Corrosion({
+ requestMiddleware: [
+ Corrosion.middleware.address([
+ 0.0.0.0,
+ 0.0.0.0
+ ]),
+ ],
+});
+```
+
+### blacklist
+ - `arr` Array of hostnames to block clients from seeing
+ - `page` Block page
+
+```javascript
+const Corrosion = require('corrosion');
+const proxy = new Corrosion({
+ requestMiddleware: [
+ Corrosion.middleware.blacklist([
+ 'example.org',
+ 'example.com',
+ ], 'Page is blocked'),
+ ],
+});
+```
diff --git a/src/Corrosion/demo/index.html b/src/Corrosion/demo/index.html
new file mode 100644
index 00000000..ad595e6e
--- /dev/null
+++ b/src/Corrosion/demo/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Corrosion/demo/index.js b/src/Corrosion/demo/index.js
new file mode 100644
index 00000000..d3a8b82b
--- /dev/null
+++ b/src/Corrosion/demo/index.js
@@ -0,0 +1,19 @@
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+const ssl = {
+ key: fs.readFileSync(path.join(__dirname, '/ssl.key')),
+ cert: fs.readFileSync(path.join(__dirname, '/ssl.cert')),
+};
+const server = https.createServer(ssl);
+const Corrosion = require('../');
+const proxy = new Corrosion({
+ codec: 'xor',
+});
+
+proxy.bundleScripts();
+
+server.on('request', (request, response) => {
+ if (request.url.startsWith(proxy.prefix)) return proxy.request(request, response);
+ response.end(fs.readFileSync(__dirname + '/index.html', 'utf-8'));
+}).on('upgrade', (clientRequest, clientSocket, clientHead) => proxy.upgrade(clientRequest, clientSocket, clientHead)).listen(443);
\ No newline at end of file
diff --git a/src/Corrosion/demo/ssl.cert b/src/Corrosion/demo/ssl.cert
new file mode 100644
index 00000000..f87b9e0f
--- /dev/null
+++ b/src/Corrosion/demo/ssl.cert
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqzCCApOgAwIBAgIJAJnCkScWtmL0MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRgwFgYDVQQKDA9UaXRhbml1bU5l
+dHdvcmsxDjAMBgNVBAsMBWdhbWVyMR4wHAYDVQQDDBUqLnRpdGFuaXVtbmV0d29y
+ay5vcmcwHhcNMjAwNjEzMTg0OTU2WhcNMjEwNjEzMTg0OTU2WjBsMQswCQYDVQQG
+EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEYMBYGA1UECgwPVGl0YW5pdW1OZXR3
+b3JrMQ4wDAYDVQQLDAVnYW1lcjEeMBwGA1UEAwwVKi50aXRhbml1bW5ldHdvcmsu
+b3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPL69+RE6r8RrFh4
+njzC8ZRnLB+yNtuGw14C0dvNb5JwgdLl5g9/wK/s0V5NGlqwxlQlxQ/gUSuYEcUR
+6MYjcnaUmZZe/gaKVV0fkfkuigOWhLnI5AQxx7rhkzx1ujuyJ9D2pkDtZpSvv0yn
+2yrvWhJMtjuxGYip8jaLuRpbXoafvR7nrlDaNcE/GwIjnCCxsRnY2bGbxYK840mN
+fuMfF2nz+fXKPuQ/9PT48e3wOo9vM5s7yKhiHYwrogqzGN4cH4sSr1FE8C7flFyT
+Yw101u7fUaopfeGCo9Pg6IrfzyzE5Qb7OlqlVk2IkvXx7pPqVc6lZCJEhOX/qF9o
+n3mFqwIDAQABo1AwTjAdBgNVHQ4EFgQUC561ob2kGtFQ4az6y64b98+Fy+IwHwYD
+VR0jBBgwFoAUC561ob2kGtFQ4az6y64b98+Fy+IwDAYDVR0TBAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAQEAotvUsSLSzFyxQz329tEPyH6Tmi19FQoA5ZbLg6EqeTI9
+08qOByDGkSYJi0npaIlPO1I557NxRzdO0PxK3ybol6lnzuSlqCJP5nb1dr0z2Eax
+wgKht9P+ap/yozU5ye05ah2nkpcaeDPnwnnWFmfsnYNfgu62EshOS+5FETWEKVUb
+LXQhGInOdJq8KZvhoLZWJoUhyAqxBfW4oVvaqs+Ff96A2NNKrvbiAVYX30rVa+x0
+KIl0/DoVvDx2Q6TiL396cAXdKUW7edRQcSsGFcxwIrU5lePm0V05aN+oCoEBvXBG
+ArPN+a5kpGjJwfcpcBVf9cJ6IsvptGS9de3eTHoTyw==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/src/Corrosion/demo/ssl.key b/src/Corrosion/demo/ssl.key
new file mode 100644
index 00000000..a878bfd4
--- /dev/null
+++ b/src/Corrosion/demo/ssl.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA8vr35ETqvxGs
+WHiePMLxlGcsH7I224bDXgLR281vknCB0uXmD3/Ar+zRXk0aWrDGVCXFD+BRK5gR
+xRHoxiNydpSZll7+BopVXR+R+S6KA5aEucjkBDHHuuGTPHW6O7In0PamQO1mlK+/
+TKfbKu9aEky2O7EZiKnyNou5Gltehp+9HueuUNo1wT8bAiOcILGxGdjZsZvFgrzj
+SY1+4x8XafP59co+5D/09Pjx7fA6j28zmzvIqGIdjCuiCrMY3hwfixKvUUTwLt+U
+XJNjDXTW7t9Rqil94YKj0+Doit/PLMTlBvs6WqVWTYiS9fHuk+pVzqVkIkSE5f+o
+X2ifeYWrAgMBAAECggEAbihK8Ev6rKr5RBQeiPjXs2SuoppV/MvIXLHHmliLKS/J
+29S0PGyM202VPtM/4dP1KMXR6nft8WmaIEsKtoKoqijZHfajtRO21pWb+JLy5wi1
+XoFTGBrs8MLZFl5mODTsuZ6rsq9O2kn5LJZvHsmcbSgVc9UQfytvG0HY840ArS3g
+kSDtUFb1xRui6wtCBKzHVvCT+FXhSBbwkHalmbqP6BefhJ3lW2VonkOcHDrdXPfW
+CEN18IJ2v8QYgXqZP6VUlAweNXLJ33ZOl+jXGdygcOG24MFqdw0VtP0XFGk0jnSS
+W6dX67BZKeZ71EKaTy02jw5LpQNXA70ismPJHQ2uQQKBgQDuROawnBIW1fC3xOle
+m+JmP0eMe0eIQycxRsMXsXhYAA0wV3qYZSLZrNK2eRhmSNt+ODSmZ2Vt11dwOv5u
+bo8WONrRlM097SmitS2S+8o7ASem2VKQzyRE72Y9517Q+aNBdLRVtjrRNSw/hfSu
+ayLuG36+yukSH7wq7mfoUX34ZwKBgQDPTrgyyw8n5XhZT/qTTRnQJ2GTvPxDzNoJ
+IAGhGJGFAb6wgLoSpGx6BC122vuRxcTjkjAiMDci5N2zNW+YZVni+F0KTVvNFfU2
+pOTJUg3luRTygCra6O02PxwpbP/9KCBAKq/kYw/eBW+gxhPwP3ZrbAirvBjgBh0I
+kIrFijNOHQKBgGUUAbFGZD4fwCCVflLOWnr5uUaVPcFGi6fR1w2EEgNy8iVh1vYz
+YVdqg3E5aepqWgLvoRY+or64LbXEsQ70A+tvbxSdxXvR0mnd5lmGS0JAuSuE4gvg
+dAhybrMwJf8NB/7KnX4G8mix3/WKxEQB2y2bqGcT+U/g+phTzuy1NXVdAoGBAIrl
+jVjK4J60iswcYCEteWwT1rbr2oF60WNnxG+xTF63apJLzWAMNnoSLnwCAKgMv/xR
+yFo/v9FrUnduCBUtYupFyeDLMATa/27bUEbq6VDPjw9jfFMr2TONWUsQMvvlVKZp
+c2wsS0dQkRhBXr6LZsZWngCiiHAg6HcCkVgFXpapAoGBAJ/8oLGt0Ar+0MTl+gyk
+xSqgHnsc5jgqhix3nIoI5oEAbfibdGmRD1S3rtWD9YsnPxMIl+6E5bOAHrmd+Zr8
+O7EP+CLvbz4JXidaaa85h9ThXSG5xk1A1UTtSFrp+KolLE1Vvmjjd+R844XsM2wZ
+OAHbihzk0iPPphjEWR4lU4Av
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/document.js b/src/Corrosion/lib/browser/document.js
new file mode 100644
index 00000000..4cc19a7b
--- /dev/null
+++ b/src/Corrosion/lib/browser/document.js
@@ -0,0 +1,271 @@
+function createDocumentRewriter(ctx) {
+ return function rewriteDocument() {
+ if (ctx.serviceWorker) return;
+ const {
+ HTMLMediaElement,
+ HTMLScriptElement,
+ HTMLAudioElement,
+ HTMLVideoElement,
+ HTMLInputElement,
+ HTMLEmbedElement,
+ HTMLTrackElement,
+ HTMLAnchorElement,
+ HTMLIFrameElement,
+ HTMLAreaElement,
+ HTMLLinkElement,
+ HTMLBaseElement,
+ HTMLFormElement,
+ HTMLImageElement,
+ HTMLSourceElement,
+ } = ctx.window;
+ const cookie = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'cookie');
+ const domain = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'domain');
+ const title = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'title');
+ const baseURI = Object.getOwnPropertyDescriptor(ctx.window.Node.prototype, 'baseURI');
+ const cookieEnabled = Object.getOwnPropertyDescriptor(ctx.window.Navigator.prototype, 'cookieEnabled');
+ let spoofTitle = '';
+ let spoofDomain = ctx.location.hostname;
+
+ if (ctx.window.Document.prototype.write) {
+ ctx.window.Document.prototype.write = new Proxy(ctx.window.Document.prototype.write, {
+ apply: (target, that , args) => {
+ if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Document.prototype.hasOwnProperty('cookie')) {
+ Object.defineProperty(ctx.window.Document.prototype, 'cookie', {
+ get: new Proxy(cookie.get, {
+ apply: (target, that, args) => {
+ const cookies = Reflect.apply(target, that, args);
+ return ctx.config.cookie ? ctx.cookies.decode(cookies, ctx.meta) : '';
+ },
+ }),
+ set: new Proxy(cookie.set, {
+ apply: (target, that, [ val ]) => {
+ return Reflect.apply(target, that, [ ctx.config.cookie ? ctx.cookies.encode(val, ctx.meta) : '' ]);
+ },
+ }),
+ });
+ };
+ if (ctx.window.Document.prototype.writeln) {
+ ctx.window.Document.prototype.writeln = new Proxy(ctx.window.Document.prototype.writeln, {
+ apply: (target, that , args) => {
+ if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Element.prototype.setAttribute) {
+ ctx.window.Element.prototype.setAttribute = new Proxy(ctx.window.Element.prototype.setAttribute, {
+ apply: (target, that, args) => {
+ if (args[0] && args[1]) {
+ const handler = ctx.html.attributeRoute({
+ name: args[0],
+ value: args[1],
+ node: that,
+ });
+ switch(handler) {
+ case 'url':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ //if (that.tagName == 'SCRIPT' && args[0] == 'src') flags.push('js');
+ args[1] = ctx.url.wrap(args[1], ctx.meta);
+ break;
+ case 'srcset':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.html.srcset(args[1], ctx.meta);
+ break;
+ case 'css':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.css.process(args[1], { ...ctx.meta, context: 'declarationList' });
+ break;
+ case 'html':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.html.process(args[1], ctx.meta);
+ break;
+ case 'delete':
+ return Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ };
+ };
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Element.prototype.getAttribute) {
+ ctx.window.Element.prototype.getAttribute = new Proxy(ctx.window.Element.prototype.getAttribute, {
+ apply: (target, that, args) => {
+ if (args[0] && that.hasAttribute(`corrosion-${args[0]}`)) args[0] = `corrosion-${args[0]}`;
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ ctx.window.CSSStyleDeclaration.prototype.setProperty = new Proxy(ctx.window.CSSStyleDeclaration.prototype.setProperty, {
+ apply: (target, that, args) => {
+ if (args[1]) args[1] = ctx.css.process(args[1], { context: 'value', ...ctx.meta, });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ if (ctx.window.Audio) {
+ ctx.window.Audio = new Proxy(ctx.window.Audio, {
+ construct: (target, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ [
+ 'innerHTML',
+ 'outerHTML',
+ ].forEach(html => {
+ const descriptor = Object.getOwnPropertyDescriptor(ctx.window.Element.prototype, html);
+ Object.defineProperty(ctx.window.Element.prototype, html, {
+ get: new Proxy(descriptor.get, {
+ apply: (target, that, args) => {
+ const body = Reflect.apply(target, that, args);
+ if (!body || html == 'innerHTML' && that.tagName == 'SCRIPT') return body;
+ return ctx.html.source(body, ctx.meta);
+ },
+ }),
+ set: new Proxy(descriptor.set, {
+ apply(target, that, [ val ]) {
+ return Reflect.apply(target, that, [ val ? ctx.html.process(val.toString(), ctx.meta) : val, ]);
+ },
+ }),
+ });
+ });
+ [
+ ['background', 'background'],
+ ['backgroundImage', 'background-image'],
+ ['listStyleImage', 'list-style-image'],
+ ].forEach(([key, cssProperty]) => {
+ Object.defineProperty(ctx.window.CSS2Properties ? ctx.window.CSS2Properties.prototype : ctx.window.CSSStyleDeclaration.prototype, key, {
+ get() {
+ return this.getPropertyValue(cssProperty);
+ },
+ set(val) {
+ return this.setProperty(cssProperty, val);
+ },
+ });
+ });
+ Object.defineProperty(ctx.window.Document.prototype, 'domain', {
+ get: new Proxy(domain.get, {
+ apply: () => spoofDomain,
+ }),
+ set: new Proxy(domain.set, {
+ apply: (target, that, [ val ]) => {
+ if (!val.toString().endsWith(ctx.location.hostname.split('.').slice(-2).join('.'))) return Reflect.apply(target, that, ['']);
+ return spoofDomain = val;
+ },
+ }),
+ });
+ if (ctx.config.title) Object.defineProperty(ctx.window.Document.prototype, 'title', {
+ get: new Proxy(title.get, {
+ apply: () => spoofTitle,
+ }),
+ set: new Proxy(title.set, {
+ apply: (target, that, [ val ]) => spoofTitle = val,
+ }),
+ });
+ Object.defineProperty(ctx.window.Navigator.prototype, 'cookieEnabled', {
+ get: new Proxy(cookieEnabled.get, {
+ apply: () => ctx.config.cookie,
+ }),
+ });
+ Object.defineProperty(ctx.window.Node.prototype, 'baseURI', {
+ get: new Proxy(baseURI.get, {
+ apply: (target, that, args) => {
+ const val = Reflect.apply(target, that, args);
+ return val.startsWith(ctx.meta.origin) ? ctx.url.unwrap(val, ctx.meta) : val;
+ },
+ }),
+ });
+ [
+ {
+ elements: [ HTMLScriptElement, HTMLMediaElement, HTMLImageElement, HTMLAudioElement, HTMLVideoElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement],
+ properties: ['src'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLFormElement ],
+ properties: ['action'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement ],
+ properties: ['href'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLImageElement, HTMLSourceElement ],
+ properties: ['srcset'],
+ handler: 'srcset',
+ },
+ {
+ elements: [ HTMLScriptElement ],
+ properties: ['integrity'],
+ handler: 'delete',
+ },
+ {
+ elements: [ HTMLIFrameElement ],
+ properties: ['contentWindow'],
+ handler: 'window',
+ },
+ ].forEach(entry => {
+ entry.elements.forEach(element => {
+ if (!element) return;
+ entry.properties.forEach(property => {
+ if (!element.prototype.hasOwnProperty(property)) return;
+ const descriptor = Object.getOwnPropertyDescriptor(element.prototype, property);
+ Object.defineProperty(element.prototype, property, {
+ get: descriptor.get ? new Proxy(descriptor.get, {
+ apply: (target, that, args) => {
+ let val = Reflect.apply(target, that, args);
+ let flags = [];
+ switch(entry.handler) {
+ case 'url':
+ //if (that.tagName == 'SCRIPT' && property == 'src') flags.push('js');
+ val = ctx.url.unwrap(val, ctx.meta);
+ break;
+ case 'srcset':
+ val = ctx.html.unsrcset(val, ctx.meta);
+ break;
+ case 'delete':
+ val = that.getAttribute(`corrosion-${property}`);
+ break;
+ case 'window':
+ try {
+ if (!val.$corrosion) {
+ val.$corrosion = new ctx.constructor({ ...ctx.config, window: val, });
+ val.$corrosion.init();
+ val.$corrosion.meta = ctx.meta;
+ };
+ } catch(e) {};
+ };
+ return val;
+ },
+ }) : undefined,
+ set: descriptor.set ? new Proxy(descriptor.set, {
+ apply(target, that, [ val ]) {
+ let newVal = val;
+ switch(entry.handler) {
+ case 'url':
+ newVal = ctx.url.wrap(newVal, ctx.meta);
+ break;
+ case 'srcset':
+ newVal = ctx.html.srcset(newVal, ctx.meta);
+ break;
+ case 'delete':
+ that.setAttribute(property, newVal);
+ return newVal;
+ };
+ return Reflect.apply(target, that, [ newVal ]);
+ },
+ }) : undefined,
+ });
+ });
+ });
+ });
+ };
+};
+module.exports = createDocumentRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/history.js b/src/Corrosion/lib/browser/history.js
new file mode 100644
index 00000000..7c2f44d6
--- /dev/null
+++ b/src/Corrosion/lib/browser/history.js
@@ -0,0 +1,26 @@
+function createHistoryRewriter(ctx) {
+ return function rewriteHistory() {
+ if (ctx.serviceWorker) return;
+ if (ctx.window.History.prototype.pushState) {
+ ctx.window.History.prototype.pushState = new Proxy(ctx.window.History.prototype.pushState, {
+ apply: (target, that, args) => {
+ if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
+ const ret = Reflect.apply(target, that, args);
+ ctx.updateLocation();
+ return ret;
+ },
+ });
+ };
+ if (ctx.window.History.prototype.replaceState) {
+ ctx.window.History.prototype.replaceState = new Proxy(ctx.window.History.prototype.replaceState, {
+ apply: (target, that, args) => {
+ if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
+ const ret = Reflect.apply(target, that, args);
+ ctx.updateLocation();
+ return ret;
+ },
+ });
+ };
+ };
+};
+module.exports = createHistoryRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/http.js b/src/Corrosion/lib/browser/http.js
new file mode 100644
index 00000000..df557b98
--- /dev/null
+++ b/src/Corrosion/lib/browser/http.js
@@ -0,0 +1,92 @@
+function createHttpRewriter(ctx = {}) {
+ return function rewriteHttp() {
+ if (ctx.window.Request) {
+ const requestURL = Object.getOwnPropertyDescriptor(ctx.window.Request.prototype, 'url');
+ ctx.window.Request = new Proxy(ctx.window.Request, {
+ construct(target, args) {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], })
+ return Reflect.construct(target, args);
+ },
+ });
+ Object.defineProperty(ctx.window.Request.prototype, 'url', {
+ get: new Proxy(requestURL.get, {
+ apply: (target, that, args) => {
+ var url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.Response) {
+ const responseURL = Object.getOwnPropertyDescriptor(ctx.window.Response.prototype, 'url');
+ Object.defineProperty(ctx.window.Response.prototype, 'url', {
+ get: new Proxy(responseURL.get, {
+ apply: (target, that, args) => {
+ var url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.open) {
+ ctx.window.open = new Proxy(ctx.window.open, {
+ apply: (target, that, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target, that, args)
+ },
+ });
+ };
+ if (ctx.window.fetch) {
+ ctx.window.fetch = new Proxy(ctx.window.fetch, {
+ apply: (target, that, args) => {
+ if (args[0] instanceof ctx.window.Request) return Reflect.apply(target, that, args);
+ if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Navigator && ctx.window.Navigator.prototype.sendBeacon) {
+ ctx.window.Navigator.prototype.sendBeacon = new Proxy(ctx.window.Navigator.prototype.sendBeacon, {
+ apply: (target, that, args) => {
+ if (args[0]) ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.XMLHttpRequest) {
+ const responseURL = Object.getOwnPropertyDescriptor(ctx.window.XMLHttpRequest.prototype, 'responseURL');
+ ctx.window.XMLHttpRequest.prototype.open = new Proxy(ctx.window.XMLHttpRequest.prototype.open, {
+ apply: (target, that, args) => {
+ if (args[1]) args[1] = ctx.url.wrap(args[1], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ Object.defineProperty(ctx.window.XMLHttpRequest.prototype, 'responseURL', {
+ get: new Proxy(responseURL.get, {
+ apply: (target, that, args) => {
+ const url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.postMessage) {
+ ctx.window.postMessage = new Proxy(ctx.window.postMessage, {
+ apply: (target, that, args) => {
+ if (!ctx.serviceWorker && args[1]) args[1] = ctx.meta.origin;
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.WebSocket && ctx.config.ws) {
+ ctx.window.WebSocket = new Proxy(ctx.window.WebSocket, {
+ construct: (target, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0].toString().replace('ws', 'http'), ctx.meta).replace('http', 'ws') + '?origin=' + ctx.location.origin;
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ };
+};
+
+module.exports = createHttpRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/index.js b/src/Corrosion/lib/browser/index.js
new file mode 100644
index 00000000..41337a39
--- /dev/null
+++ b/src/Corrosion/lib/browser/index.js
@@ -0,0 +1,195 @@
+const createDocumentRewriter = require('./document');
+const createHistoryRewriter = require('./history');
+const createHttpRewriter = require('./http');
+const createLocation = require('./location');
+const createWorkerRewriter = require('./worker');
+const defaultConfig = {
+ prefix: '/service/',
+ codec: 'plain',
+ ws: true,
+ cookie: true,
+ title: 'Service',
+ serviceWorker: false,
+ url: null,
+ window: false,
+};
+
+class Corrosion extends require('../rewrite') {
+ constructor(config = defaultConfig) {
+ super(Object.assign(defaultConfig, config));
+ const corrosion = this;
+ if (!this.config.window) throw 'Corrosion Error: No window was given.';
+ this.serviceWorker = this.config.serviceWorker || false;
+ this.window = this.config.window;
+ this.document = this.serviceWorker ? this.window.document : {};
+ this._url = new URL((this.config.url || this.url.unwrap(this.window.location.href, { origin: this.window.location.origin, })));
+ this.originalXhr = this.window.XMLHttpRequest;
+ this.meta = {
+ origin: this.window.location.origin,
+ get base() {
+ if (corrosion.serviceWorker) return corrosion._url;
+ return corrosion.window.document.baseURI != corrosion.location.href ? new URL(corrosion.window.document.baseURI) : corrosion._url;
+ },
+ url: this._url,
+ };
+ this.location = createLocation(this, this._url);
+ this.rewriteHttp = createHttpRewriter(this);
+ this.rewriteDocument = createDocumentRewriter(this);
+ this.rewriteHistory = createHistoryRewriter(this);
+ this.rewriteWorker = createWorkerRewriter(this);
+ if (!this.serviceWorker && this.window.document.currentScript) this.window.document.currentScript.remove();
+ };
+ get parent() {
+ if (this.serviceWorker) return false;
+ try {
+ return this.window.parent.$corrosion ? this.window.parent : this.window;
+ } catch(e) {
+ return this.window;
+ };
+ };
+ get top() {
+ if (this.serviceWorker) return false;
+ try {
+ return this.window.top.$corrosion ? this.window.top : this.parent;
+ } catch(e) {
+ return this.parent;
+ };
+ };
+ init() {
+ this.rewriteHttp();
+ this.rewriteDocument();
+ this.rewriteHistory();
+ this.rewriteWorker();
+ this.window.Location = createLocation.Location;
+ this.window.$corrosionGet$ = this.get$.bind(this);
+ this.window.$corrosionSet$ = this.set$.bind(this);
+ this.window.$corrosionGet$m = this.get$m.bind(this);
+ this.window.$corrosionSet$m = this.set$m.bind(this);
+ this.window.$corrosionCall$m = this.call$m.bind(this);
+ };
+ get$m(obj, key) {
+ if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
+ return this.parent.$corrosion.get$m(this.parent, key);
+ };
+ if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
+ return this.top.$corrosion.get$m(this.top, key);
+ };
+ if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') return this.location;
+ if (!this.serviceWorker && obj == this.window && key == 'parent' && this.window != this.window.parent) return this.parent;
+ if (!this.serviceWorker && obj == this.window && key == 'top' && this.window != this.window.top) return this.top;
+ return obj[key];
+ };
+ set$m(obj, key, val, operator) {
+ if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
+ return this.parent.$corrosion.set$m(this.parent, key, val, operator);
+ };
+ if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
+ return this.top.$corrosion.set$m(this.top, key, val, operator);
+ };
+ if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') obj = this;
+ switch(operator) {
+ case '+=':
+ return obj[key] += val;
+ case '-=':
+ return obj[key] -= val;
+ case '*=':
+ return obj[key] *= val;
+ case '/=':
+ return obj[key] /= val;
+ case '%=':
+ return obj[key] %= val;
+ case '**=':
+ return obj[key] **= val;
+ case '<<=':
+ return obj[key] <<= val;
+ case '>>=':
+ return obj[key] >>= val;
+ case '>>>=':
+ return obj[key] >>>= val;
+ case '&=':
+ return obj[key] &= val;
+ case '^=':
+ return obj[key] ^= val;
+ case '|=':
+ return obj[key] |= val;
+ case '&&=':
+ return obj[key] &&= val;
+ case '||=':
+ return obj[key] ||= val;
+ case '??=':
+ return obj[key] ??= val;
+ case '++':
+ return obj[key]++;
+ case '--':
+ return obj[key]--;
+ case '=':
+ default:
+ return obj[key] = val;
+ };
+ };
+ call$m(obj, key, args) {
+ if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
+ return this.parent.$corrosion.call$m(this.parent, key, args);
+ };
+ if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
+ return this.top.$corrosion.call$m(this.top, key, args);
+ };
+ return obj[key](...args);
+ };
+ get$(obj) {
+ if (obj == this.window.location) return this.location;
+ if (!this.serviceWorker && obj == this.window.parent) return this.parent;
+ if (!this.serviceWorker && obj == this.window.top) return this.top;
+ return obj;
+ };
+ set$(obj, val, operator) {
+ if (obj == this.window.location) return this.set$(this.location, val, operator);
+ if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) return this.parent.set$(this.parent, val, operator);
+ if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) return this.top.set$(this.top, val, operator);
+ switch(operator) {
+ case '+=':
+ return obj += val;
+ case '-=':
+ return obj -= val;
+ case '*=':
+ return obj *= val;
+ case '/=':
+ return obj /= val;
+ case '%=':
+ return obj %= val;
+ case '**=':
+ return obj **= val;
+ case '<<=':
+ return obj <<= val;
+ case '>>=':
+ return obj >>= val;
+ case '>>>=':
+ return obj >>>= val;
+ case '&=':
+ return obj &= val;
+ case '^=':
+ return obj ^= val;
+ case '|=':
+ return obj |= val;
+ case '&&=':
+ return obj &&= val;
+ case '||=':
+ return obj ||= val;
+ case '??=':
+ return obj ??= val;
+ case '++':
+ return obj++;
+ case '--':
+ return obj--;
+ case '=':
+ default:
+ return obj = val;
+ };
+ };
+ updateLocation() {
+ this._url = new URL(this.url.unwrap(this.window.location.href, this.meta));
+ this.location = createLocation(this, this._url);
+ };
+};
+
+globalThis.Corrosion = Corrosion;
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/location.js b/src/Corrosion/lib/browser/location.js
new file mode 100644
index 00000000..02d7c5a0
--- /dev/null
+++ b/src/Corrosion/lib/browser/location.js
@@ -0,0 +1,56 @@
+class Location {
+ get [Symbol.toPrimitive]() {
+ return () => this.href;
+ };
+};
+
+function createLocation(ctx, url) {
+ const _location = new Location();
+ const _url = new URL(url);
+ [
+ 'hash',
+ 'host',
+ 'hostname',
+ 'href',
+ 'pathname',
+ 'port',
+ 'protocol',
+ 'search',
+ 'origin',
+ ].forEach(property => {
+ Object.defineProperty(_location, property, {
+ get() {
+ return _url[property];
+ },
+ set(val) {
+ if (ctx.serviceWorker || property == 'origin') return;
+ if (property == 'href') {
+ return ctx.window.location.href = ctx.url.wrap(new URL(val, _url).href);
+ };
+ _url[property] = val;
+ return ctx.window.location.href = ctx.url.wrap(_url);
+ },
+ });
+ });
+ if (!ctx.serviceWorker) [
+ 'assign',
+ 'replace',
+ 'reload',
+ ].forEach(method => {
+ _location[method] = new Proxy(ctx.window.location[method], {
+ apply(target, that, args) {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target.bind(ctx.window.location), that, args);
+ },
+ });
+ });
+ _location.toString = new Proxy(_url.toString, {
+ apply(target, that, args) {
+ return Reflect.apply(target.bind(_url), that, args);
+ },
+ });
+ return _location;
+};
+
+createLocation.Location = Location;
+module.exports = createLocation;
\ No newline at end of file
diff --git a/src/Corrosion/lib/browser/worker.js b/src/Corrosion/lib/browser/worker.js
new file mode 100644
index 00000000..c65486c0
--- /dev/null
+++ b/src/Corrosion/lib/browser/worker.js
@@ -0,0 +1,33 @@
+function createWorkerRewriter(ctx = {}) {
+ return function rewriteWorker() {
+ if (ctx.window.Worker) {
+ ctx.window.Worker = new Proxy(ctx.window.Worker, {
+ construct: (target, args) => {
+ if (args[0]) {
+ if (args[0].trim().startsWith(`blob:${ctx.window.location.origin}`)) {
+ const xhr = new ctx.originalXhr();
+ xhr.open('GET', args[0], false);
+ xhr.send();
+ const script = ctx.js.process(xhr.responseText, ctx.location.origin + args[0].trim().slice(`blob:${ctx.window.location.origin}`.length));
+ const blob = new Blob([ script ], { type: 'application/javascript' });
+ args[0] = URL.createObjectURL(blob);
+ } else {
+ args[0] = ctx.url.wrap(args[0], ctx.meta);
+ };
+ };
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ if (ctx.serviceWorker && ctx.window.importScripts) {
+ ctx.window.importScripts = new Proxy(ctx.window.importScripts, {
+ apply: (target, that, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ };
+};
+
+module.exports = createWorkerRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/codec.js b/src/Corrosion/lib/codec.js
new file mode 100644
index 00000000..a3fc2742
--- /dev/null
+++ b/src/Corrosion/lib/codec.js
@@ -0,0 +1,66 @@
+exports.xor = {
+ encode(str){
+ if (!str) return str;
+ return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join(''));
+ },
+ decode(str){
+ if (!str) return str;
+ return decodeURIComponent(str).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join('');
+ },
+};
+exports.plain = {
+ encode(str) {
+ if (!str) return str;
+ return encodeURIComponent(str);
+ },
+ decode(str) {
+ if (!str) return str;
+ return decodeURIComponent(str);
+ },
+};
+exports.base64 = {
+ encode(str){
+ if (!str) return str;
+ str = str.toString();
+ const b64chs = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
+ let u32;
+ let c0;
+ let c1;
+ let c2;
+ let asc = '';
+ let pad = str.length % 3;
+
+ for (let i = 0; i < str.length;) {
+ if((c0 = str.charCodeAt(i++)) > 255 || (c1 = str.charCodeAt(i++)) > 255 || (c2 = str.charCodeAt(i++)) > 255)throw new TypeError('invalid character found');
+ u32 = (c0 << 16) | (c1 << 8) | c2;
+ asc += b64chs[u32 >> 18 & 63]
+ + b64chs[u32 >> 12 & 63]
+ + b64chs[u32 >> 6 & 63]
+ + b64chs[u32 & 63];
+ }
+
+ return encodeURIComponent(pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc);
+ },
+ decode(str){
+ if (!str) return str;
+ str = decodeURIComponent(str.toString());
+ const b64tab = {"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"+":62,"/":63,"=":64};
+ str = str.replace(/\s+/g, '');
+ str += '=='.slice(2 - (str.length & 3));
+ let u24;
+ let bin = '';
+ let r1;
+ let r2;
+
+ for (let i = 0; i < str.length;) {
+ u24 = b64tab[str.charAt(i++)] << 18
+ | b64tab[str.charAt(i++)] << 12
+ | (r1 = b64tab[str.charAt(i++)]) << 6
+ | (r2 = b64tab[str.charAt(i++)]);
+ bin += r1 === 64 ? String.fromCharCode(u24 >> 16 & 255)
+ : r2 === 64 ? String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255)
+ : String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
+ };
+ return bin;
+ },
+};
\ No newline at end of file
diff --git a/src/Corrosion/lib/cookie-parser.js b/src/Corrosion/lib/cookie-parser.js
new file mode 100644
index 00000000..c11dfe7d
--- /dev/null
+++ b/src/Corrosion/lib/cookie-parser.js
@@ -0,0 +1,95 @@
+// -------------------
+// This file is shared both by the server and client.
+// Do not include any browser or node specific APIs
+// -------------------
+
+class CookieStore {
+ constructor(val = ''){
+ this.data = {};
+ val.split(';').map(cookie => {
+ var [ name, val = ''] = cookie.trimStart().split('=');
+ if (name) this.data[name] = val;
+ });
+ };
+ has(name){
+ if (!name || !this.data[name]) return false;
+ return true;
+ };
+ get(name){
+ return this.has(name) ? this.data[name] : null;
+ };
+ set(name, val){
+ if (!name || !val) return;
+ return this.data[name] = val;
+ };
+ delete(name){
+ if (!name) return;
+ return delete this.data[name];
+ };
+ forEach(action = (node, key) => null){
+ for (let prop in this.data) action(this.data[prop], prop);
+ };
+ serialize(){
+ var str = '';
+ for (let i in this.data) str += ` ${i}=${this.data[i]};`;
+ return str.substr(1);
+ };
+};
+
+class SetCookie {
+ constructor(val = ''){
+
+ var [ [ name, value = '' ], ...data ] = val.split(';').map(str => str.trimStart().split('='));
+
+ this.name = name;
+ this.value = value;
+ this.expires = null;
+ this.maxAge = null;
+ this.domain = null;
+ this.secure = false;
+ this.httpOnly = false;
+ this.path = null;
+ this.sameSite = null;
+
+ data.forEach(([name = null, value = null]) => {
+ if (typeof name == 'string') switch(name.toLowerCase()){
+ case 'domain':
+ this.domain = value;
+ break;
+ case 'secure':
+ this.secure = true;
+ break;
+ case 'httponly':
+ this.httpOnly = true;
+ break;
+ case 'samesite':
+ this.sameSite = value;
+ break;
+ case 'path':
+ this.path = value;
+ break;
+ case 'expires':
+ this.expires = value;
+ break;
+ case 'maxage':
+ this.maxAge = value;
+ break;
+ };
+ });
+ };
+ serialize(){
+ if (!this.name) return;
+ var str = `${this.name}=${this.value};`;
+ if (this.expires) str += ` Expires=${this.expires};`;
+ if (this.maxAge) str += ` Max-Age=${this.max_age};`;
+ if (this.domain) str += ` Domain=${this.domain};`;
+ if (this.secure) str += ` Secure;`;
+ if (this.httpOnly) str += ` HttpOnly;`;
+ if (this.path) str += ` Path=${this.path};`;
+ if (this.sameSite) str += ` SameSite=${this.sameSite};`;
+ return str;
+ };
+};
+
+exports.CookieStore = CookieStore;
+exports.SetCookie = SetCookie;
\ No newline at end of file
diff --git a/src/Corrosion/lib/cookie.js b/src/Corrosion/lib/cookie.js
new file mode 100644
index 00000000..1d505968
--- /dev/null
+++ b/src/Corrosion/lib/cookie.js
@@ -0,0 +1,34 @@
+const { SetCookie, CookieStore } = require('./cookie-parser');
+
+class CookieRewriter {
+ constructor(ctx) {
+ this.ctx = ctx;
+ };
+ decode(store, config = {}) {
+ const url = new URL(config.url);
+ const cookies = new CookieStore(store);
+ cookies.forEach((val, key) => {
+ if (!key.includes('@') || key.slice(key.length - url.hostname.length) != url.hostname) return cookies.delete(key);
+ cookies.delete(key);
+ cookies.set(key.substr(0, key.length - url.hostname.length - 1), val);
+ });
+ return cookies.serialize();
+ };
+ encode(input, config = {}) {
+ if (Array.isArray(input)) {
+ const rw = [ ...input ];
+ for (let i in rw) rw[i] = this.encode(rw[i], config);
+ return rw;
+ };
+ const url = new URL(config.url);
+ const cookie = new SetCookie(input);
+ if (!cookie.name) return null;
+ cookie.domain = config.domain;
+ cookie.secure = config.secure;
+ cookie.name += `@${url.hostname}`;
+ cookie.path = this.ctx.prefix;
+ return cookie.serialize() || '';
+ };
+};
+
+module.exports = CookieRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/css.js b/src/Corrosion/lib/css.js
new file mode 100644
index 00000000..7604eb88
--- /dev/null
+++ b/src/Corrosion/lib/css.js
@@ -0,0 +1,67 @@
+const csstree = require('css-tree');
+
+class CSSRewriter {
+ constructor(ctx) {
+ this.ctx = ctx;
+ };
+ process(source, config = {}) {
+ const ast = csstree.parse(source, {
+ context: config.context || 'stylesheet',
+ parseCustomProperty: true,
+ });
+ const urls = csstree.findAll(ast, node =>
+ node.type == 'Url'
+ );
+ const imports = csstree.findAll(ast, node =>
+ node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
+ );
+ urls.forEach(({ value }) => {
+ switch(value.type) {
+ case 'String':
+ const quote = value.value.substring(0, 1);
+ value.value = quote + this.ctx.url.wrap(value.value.slice(1).slice(0, -1), config) + quote;
+ break;
+ case 'Raw':
+ value.value = this.ctx.url.wrap(value.value, config);
+ break;
+ };
+ });
+ imports.forEach(({ prelude }) => {
+ const { data } = prelude.children.head;
+ const quote = data.value.substring(0, 1);
+ data.value = quote + this.ctx.url.wrap(data.value.slice(1).slice(0, -1), config) + quote;
+ });
+ return csstree.generate(ast);
+ };
+ source(processed, config = {}) {
+ const ast = csstree.parse(processed, {
+ context: config.context || 'stylesheet',
+ parseCustomProperty: true,
+ });
+ const urls = csstree.findAll(ast, node =>
+ node.type == 'Url'
+ );
+ const imports = csstree.findAll(ast, node =>
+ node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
+ );
+ urls.forEach(({ value }) => {
+ switch(value.type) {
+ case 'String':
+ const quote = value.value.substring(0, 1);
+ value.value = quote + this.ctx.url.unwrap(value.value.slice(1).slice(0, -1), config) + quote;
+ break;
+ case 'Raw':
+ value.value = this.ctx.url.unwrap(value.value, config);
+ break;
+ };
+ });
+ imports.forEach(({ prelude }) => {
+ const { data } = prelude.children.head;
+ const quote = data.value.substring(0, 1);
+ data.value = quote + this.ctx.url.unwrap(data.value.slice(1).slice(0, -1), config) + quote;
+ });
+ return csstree.generate(ast);
+ };
+};
+
+module.exports = CSSRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/esotope.js b/src/Corrosion/lib/esotope.js
new file mode 100644
index 00000000..21040764
--- /dev/null
+++ b/src/Corrosion/lib/esotope.js
@@ -0,0 +1,2562 @@
+// -------------------------------------------------------------
+// WARNING: this file is used by both the client and the server.
+// Do not use any browser or node-specific API!
+// -------------------------------------------------------------
+
+/*
+ Copyright (C) 2014 Ivan Nikulin
+ Copyright (C) 2012-2014 Yusuke Suzuki
+ Copyright (C) 2012-2013 Michael Ficarra
+ Copyright (C) 2012-2013 Mathias Bynens
+ Copyright (C) 2013 Irakli Gozalishvili
+ Copyright (C) 2012 Robert Gust-Bardon
+ Copyright (C) 2012 John Freeman
+ Copyright (C) 2011-2012 Ariya Hidayat
+ Copyright (C) 2012 Joost-Wim Boekesteijn
+ Copyright (C) 2012 Kris Kowal
+ Copyright (C) 2012 Arpad Borsos
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ 'use strict';
+
+ var isArray,
+ json,
+ renumber,
+ hexadecimal,
+ quotes,
+ escapeless,
+ parentheses,
+ semicolons,
+ safeConcatenation,
+ directive,
+ extra,
+ parse;
+
+ var Syntax = {
+ AssignmentExpression: 'AssignmentExpression',
+ AssignmentPattern: 'AssignmentPattern',
+ ArrayExpression: 'ArrayExpression',
+ ArrayPattern: 'ArrayPattern',
+ ArrowFunctionExpression: 'ArrowFunctionExpression',
+ AwaitExpression: 'AwaitExpression',
+ BlockStatement: 'BlockStatement',
+ BinaryExpression: 'BinaryExpression',
+ BreakStatement: 'BreakStatement',
+ CallExpression: 'CallExpression',
+ CatchClause: 'CatchClause',
+ ClassBody: 'ClassBody',
+ ClassDeclaration: 'ClassDeclaration',
+ ClassExpression: 'ClassExpression',
+ ComprehensionBlock: 'ComprehensionBlock',
+ ComprehensionExpression: 'ComprehensionExpression',
+ ConditionalExpression: 'ConditionalExpression',
+ ContinueStatement: 'ContinueStatement',
+ DirectiveStatement: 'DirectiveStatement',
+ DoWhileStatement: 'DoWhileStatement',
+ DebuggerStatement: 'DebuggerStatement',
+ EmptyStatement: 'EmptyStatement',
+ ExportAllDeclaration: 'ExportAllDeclaration',
+ ExportBatchSpecifier: 'ExportBatchSpecifier',
+ ExportDeclaration: 'ExportDeclaration',
+ ExportNamedDeclaration: 'ExportNamedDeclaration',
+ ExportSpecifier: 'ExportSpecifier',
+ ExpressionStatement: 'ExpressionStatement',
+ ForStatement: 'ForStatement',
+ ForInStatement: 'ForInStatement',
+ ForOfStatement: 'ForOfStatement',
+ FunctionDeclaration: 'FunctionDeclaration',
+ FunctionExpression: 'FunctionExpression',
+ GeneratorExpression: 'GeneratorExpression',
+ Identifier: 'Identifier',
+ IfStatement: 'IfStatement',
+ ImportExpression: 'ImportExpression',
+ ImportSpecifier: 'ImportSpecifier',
+ ImportDeclaration: 'ImportDeclaration',
+ ChainExpression: 'ChainExpression',
+ Literal: 'Literal',
+ LabeledStatement: 'LabeledStatement',
+ LogicalExpression: 'LogicalExpression',
+ MemberExpression: 'MemberExpression',
+ MetaProperty: 'MetaProperty',
+ MethodDefinition: 'MethodDefinition',
+ ModuleDeclaration: 'ModuleDeclaration',
+ NewExpression: 'NewExpression',
+ ObjectExpression: 'ObjectExpression',
+ ObjectPattern: 'ObjectPattern',
+ Program: 'Program',
+ Property: 'Property',
+ RestElement: 'RestElement',
+ ReturnStatement: 'ReturnStatement',
+ SequenceExpression: 'SequenceExpression',
+ SpreadElement: 'SpreadElement',
+ Super: 'Super',
+ SwitchStatement: 'SwitchStatement',
+ SwitchCase: 'SwitchCase',
+ TaggedTemplateExpression: 'TaggedTemplateExpression',
+ TemplateElement: 'TemplateElement',
+ TemplateLiteral: 'TemplateLiteral',
+ ThisExpression: 'ThisExpression',
+ ThrowStatement: 'ThrowStatement',
+ TryStatement: 'TryStatement',
+ UnaryExpression: 'UnaryExpression',
+ UpdateExpression: 'UpdateExpression',
+ VariableDeclaration: 'VariableDeclaration',
+ VariableDeclarator: 'VariableDeclarator',
+ WhileStatement: 'WhileStatement',
+ WithStatement: 'WithStatement',
+ YieldExpression: 'YieldExpression'
+ };
+
+ exports.Syntax = Syntax;
+
+ var Precedence = {
+ Sequence: 0,
+ Yield: 1,
+ Assignment: 1,
+ Conditional: 2,
+ ArrowFunction: 2,
+ Coalesce: 3,
+ LogicalOR: 3,
+ LogicalAND: 4,
+ BitwiseOR: 5,
+ BitwiseXOR: 6,
+ BitwiseAND: 7,
+ Equality: 8,
+ Relational: 9,
+ BitwiseSHIFT: 10,
+ Additive: 11,
+ Multiplicative: 12,
+ Unary: 13,
+ Exponentiation: 14,
+ Postfix: 14,
+ Await: 14,
+ Call: 15,
+ New: 16,
+ TaggedTemplate: 17,
+ OptionalChaining: 17,
+ Member: 18,
+ Primary: 19
+ };
+
+ var BinaryPrecedence = {
+ '||': Precedence.LogicalOR,
+ '&&': Precedence.LogicalAND,
+ '|': Precedence.BitwiseOR,
+ '^': Precedence.BitwiseXOR,
+ '&': Precedence.BitwiseAND,
+ '==': Precedence.Equality,
+ '!=': Precedence.Equality,
+ '===': Precedence.Equality,
+ '!==': Precedence.Equality,
+ 'is': Precedence.Equality,
+ 'isnt': Precedence.Equality,
+ '<': Precedence.Relational,
+ '>': Precedence.Relational,
+ '<=': Precedence.Relational,
+ '>=': Precedence.Relational,
+ 'in': Precedence.Relational,
+ 'instanceof': Precedence.Relational,
+ '<<': Precedence.BitwiseSHIFT,
+ '>>': Precedence.BitwiseSHIFT,
+ '>>>': Precedence.BitwiseSHIFT,
+ '+': Precedence.Additive,
+ '-': Precedence.Additive,
+ '*': Precedence.Multiplicative,
+ '%': Precedence.Multiplicative,
+ '/': Precedence.Multiplicative,
+ '??': Precedence.Coalesce,
+ '**': Precedence.Exponentiation
+ };
+
+ function getDefaultOptions () {
+ // default options
+ return {
+ indent: null,
+ base: null,
+ parse: null,
+ format: {
+ indent: {
+ style: ' ',
+ base: 0
+ },
+ newline: '\n',
+ space: ' ',
+ json: false,
+ renumber: false,
+ hexadecimal: false,
+ quotes: 'single',
+ escapeless: false,
+ compact: false,
+ parentheses: true,
+ semicolons: true,
+ safeConcatenation: false
+ },
+ directive: false,
+ raw: true,
+ verbatim: null
+ };
+ }
+
+ //-------------------------------------------------===------------------------------------------------------
+ // Lexical utils
+ //-------------------------------------------------===------------------------------------------------------
+
+ //Const
+ var NON_ASCII_WHITESPACES = [
+ 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
+ 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000,
+ 0xFEFF
+ ];
+
+ //Regular expressions
+ var NON_ASCII_IDENTIFIER_CHARACTERS_REGEXP = new RegExp(
+ '[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376' +
+ '\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-' +
+ '\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA' +
+ '\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-' +
+ '\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-' +
+ '\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-' +
+ '\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-' +
+ '\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38' +
+ '\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83' +
+ '\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9' +
+ '\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-' +
+ '\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-' +
+ '\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E' +
+ '\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-' +
+ '\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-' +
+ '\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-' +
+ '\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE' +
+ '\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44' +
+ '\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-' +
+ '\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A' +
+ '\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-' +
+ '\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9' +
+ '\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84' +
+ '\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-' +
+ '\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5' +
+ '\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-' +
+ '\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-' +
+ '\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD' +
+ '\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B' +
+ '\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E' +
+ '\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-' +
+ '\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-' +
+ '\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-' +
+ '\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F' +
+ '\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115' +
+ '\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188' +
+ '\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-' +
+ '\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-' +
+ '\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A' +
+ '\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5' +
+ '\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697' +
+ '\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873' +
+ '\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-' +
+ '\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-' +
+ '\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC' +
+ '\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-' +
+ '\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D' +
+ '\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74' +
+ '\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-' +
+ '\uFFD7\uFFDA-\uFFDC]'
+ );
+
+
+ //Methods
+ function isIdentifierCh (cp) {
+ if (cp < 0x80) {
+ return cp >= 97 && cp <= 122 || // a..z
+ cp >= 65 && cp <= 90 || // A..Z
+ cp >= 48 && cp <= 57 || // 0..9
+ cp === 36 || cp === 95 || // $ (dollar) and _ (underscore)
+ cp === 92; // \ (backslash)
+ }
+
+ var ch = String.fromCharCode(cp);
+
+ return NON_ASCII_IDENTIFIER_CHARACTERS_REGEXP.test(ch);
+ }
+
+ function isLineTerminator (cp) {
+ return cp === 0x0A || cp === 0x0D || cp === 0x2028 || cp === 0x2029;
+ }
+
+ function isWhitespace (cp) {
+ return cp === 0x20 || cp === 0x09 || isLineTerminator(cp) || cp === 0x0B || cp === 0x0C || cp === 0xA0 ||
+ (cp >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(cp) >= 0);
+ }
+
+ function isDecimalDigit (cp) {
+ return cp >= 48 && cp <= 57;
+ }
+
+ function stringRepeat (str, num) {
+ var result = '';
+
+ for (num |= 0; num > 0; num >>>= 1, str += str) {
+ if (num & 1) {
+ result += str;
+ }
+ }
+
+ return result;
+ }
+
+ isArray = Array.isArray;
+ if (!isArray) {
+ isArray = function isArray (array) {
+ return Object.prototype.toString.call(array) === '[object Array]';
+ };
+ }
+
+
+ function updateDeeply (target, override) {
+ var key, val;
+
+ function isHashObject (target) {
+ return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
+ }
+
+ for (key in override) {
+ if (override.hasOwnProperty(key)) {
+ val = override[key];
+ if (isHashObject(val)) {
+ if (isHashObject(target[key])) {
+ updateDeeply(target[key], val);
+ }
+ else {
+ target[key] = updateDeeply({}, val);
+ }
+ }
+ else {
+ target[key] = val;
+ }
+ }
+ }
+ return target;
+ }
+
+ function generateNumber (value) {
+ var result, point, temp, exponent, pos;
+
+ if (value === 1 / 0) {
+ return json ? 'null' : renumber ? '1e400' : '1e+400';
+ }
+
+ result = '' + value;
+ if (!renumber || result.length < 3) {
+ return result;
+ }
+
+ point = result.indexOf('.');
+ //NOTE: 0x30 == '0'
+ if (!json && result.charCodeAt(0) === 0x30 && point === 1) {
+ point = 0;
+ result = result.slice(1);
+ }
+ temp = result;
+ result = result.replace('e+', 'e');
+ exponent = 0;
+ if ((pos = temp.indexOf('e')) > 0) {
+ exponent = +temp.slice(pos + 1);
+ temp = temp.slice(0, pos);
+ }
+ if (point >= 0) {
+ exponent -= temp.length - point - 1;
+ temp = +(temp.slice(0, point) + temp.slice(point + 1)) + '';
+ }
+ pos = 0;
+
+ //NOTE: 0x30 == '0'
+ while (temp.charCodeAt(temp.length + pos - 1) === 0x30) {
+ --pos;
+ }
+ if (pos !== 0) {
+ exponent -= pos;
+ temp = temp.slice(0, pos);
+ }
+ if (exponent !== 0) {
+ temp += 'e' + exponent;
+ }
+ if ((temp.length < result.length ||
+ (hexadecimal && value > 1e12 && Math.floor(value) === value &&
+ (temp = '0x' + value.toString(16)).length
+ < result.length)) &&
+ +temp === value) {
+ result = temp;
+ }
+
+ return result;
+ }
+
+ // Generate valid RegExp expression.
+ // This function is based on https://github.com/Constellation/iv Engine
+
+ function escapeRegExpCharacter (ch, previousIsBackslash) {
+ // not handling '\' and handling \u2028 or \u2029 to unicode escape sequence
+ if ((ch & ~1) === 0x2028) {
+ return (previousIsBackslash ? 'u' : '\\u') + ((ch === 0x2028) ? '2028' : '2029');
+ }
+ else if (ch === 10 || ch === 13) { // \n, \r
+ return (previousIsBackslash ? '' : '\\') + ((ch === 10) ? 'n' : 'r');
+ }
+ return String.fromCharCode(ch);
+ }
+
+ function generateRegExp (reg) {
+ var match, result, flags, i, iz, ch, characterInBrack, previousIsBackslash;
+
+ result = reg.toString();
+
+ if (reg.source) {
+ // extract flag from toString result
+ match = result.match(/\/([^/]*)$/);
+ if (!match) {
+ return result;
+ }
+
+ flags = match[1];
+ result = '';
+
+ characterInBrack = false;
+ previousIsBackslash = false;
+ for (i = 0, iz = reg.source.length; i < iz; ++i) {
+ ch = reg.source.charCodeAt(i);
+
+ if (!previousIsBackslash) {
+ if (characterInBrack) {
+ if (ch === 93) { // ]
+ characterInBrack = false;
+ }
+ }
+ else {
+ if (ch === 47) { // /
+ result += '\\';
+ }
+ else if (ch === 91) { // [
+ characterInBrack = true;
+ }
+ }
+ result += escapeRegExpCharacter(ch, previousIsBackslash);
+ previousIsBackslash = ch === 92; // \
+ }
+ else {
+ // if new RegExp("\\\n') is provided, create /\n/
+ result += escapeRegExpCharacter(ch, previousIsBackslash);
+ // prevent like /\\[/]/
+ previousIsBackslash = false;
+ }
+ }
+
+ return '/' + result + '/' + flags;
+ }
+
+ return result;
+ }
+
+ function escapeAllowedCharacter (code, next) {
+ var hex, result = '\\';
+
+ switch (code) {
+ case 0x08: // \b
+ result += 'b';
+ break;
+ case 0x0C: // \f
+ result += 'f';
+ break;
+ case 0x09: // \t
+ result += 't';
+ break;
+ default:
+ hex = code.toString(16).toUpperCase();
+ if (json || code > 0xFF) {
+ result += 'u' + '0000'.slice(hex.length) + hex;
+ }
+
+ else if (code === 0x0000 && !isDecimalDigit(next)) {
+ result += '0';
+ }
+
+ else if (code === 0x000B) { // \v
+ result += 'x0B';
+ }
+
+ else {
+ result += 'x' + '00'.slice(hex.length) + hex;
+ }
+ break;
+ }
+
+ return result;
+ }
+
+ function escapeDisallowedCharacter (code) {
+ var result = '\\';
+ switch (code) {
+ case 0x5C // \
+ :
+ result += '\\';
+ break;
+ case 0x0A // \n
+ :
+ result += 'n';
+ break;
+ case 0x0D // \r
+ :
+ result += 'r';
+ break;
+ case 0x2028:
+ result += 'u2028';
+ break;
+ case 0x2029:
+ result += 'u2029';
+ break;
+ }
+
+ return result;
+ }
+
+ function escapeDirective (str) {
+ var i, iz, code, quote;
+
+ quote = quotes === 'double' ? '"' : '\'';
+ for (i = 0, iz = str.length; i < iz; ++i) {
+ code = str.charCodeAt(i);
+ if (code === 0x27) { // '
+ quote = '"';
+ break;
+ }
+ else if (code === 0x22) { // "
+ quote = '\'';
+ break;
+ }
+ else if (code === 0x5C) { // \
+ ++i;
+ }
+ }
+
+ return quote + str + quote;
+ }
+
+ function escapeString (str) {
+ var result = '', i, len, code, singleQuotes = 0, doubleQuotes = 0, single, quote;
+ //TODO http://jsperf.com/character-counting/8
+ for (i = 0, len = str.length; i < len; ++i) {
+ code = str.charCodeAt(i);
+ if (code === 0x27) { // '
+ ++singleQuotes;
+ }
+ else if (code === 0x22) { // "
+ ++doubleQuotes;
+ }
+ else if (code === 0x2F && json) { // /
+ result += '\\';
+ }
+ else if (isLineTerminator(code) || code === 0x5C) { // \
+ result += escapeDisallowedCharacter(code);
+ continue;
+ }
+ else if ((json && code < 0x20) || // SP
+ !(json || escapeless || (code >= 0x20 && code <= 0x7E))) { // SP, ~
+ result += escapeAllowedCharacter(code, str.charCodeAt(i + 1));
+ continue;
+ }
+ result += String.fromCharCode(code);
+ }
+
+ single = !(quotes === 'double' || (quotes === 'auto' && doubleQuotes < singleQuotes));
+ quote = single ? '\'' : '"';
+
+ if (!(single ? singleQuotes : doubleQuotes)) {
+ return quote + result + quote;
+ }
+
+ str = result;
+ result = quote;
+
+ for (i = 0, len = str.length; i < len; ++i) {
+ code = str.charCodeAt(i);
+ if ((code === 0x27 && single) || (code === 0x22 && !single)) { // ', "
+ result += '\\';
+ }
+ result += String.fromCharCode(code);
+ }
+
+ return result + quote;
+ }
+
+
+ function join (l, r) {
+ if (!l.length)
+ return r;
+
+ if (!r.length)
+ return l;
+
+ var lCp = l.charCodeAt(l.length - 1),
+ rCp = r.charCodeAt(0);
+
+ if (isIdentifierCh(lCp) && isIdentifierCh(rCp) ||
+ lCp === rCp && (lCp === 0x2B || lCp === 0x2D) || // + +, - -
+ lCp === 0x2F && rCp === 0x69) { // /re/ instanceof foo
+ return l + _.space + r;
+ }
+
+ else if (isWhitespace(lCp) || isWhitespace(rCp))
+ return l + r;
+
+ return l + _.optSpace + r;
+ }
+
+ function shiftIndent () {
+ var prevIndent = _.indent;
+
+ _.indent += _.indentUnit;
+ return prevIndent;
+ }
+
+ function adoptionPrefix ($stmt) {
+ if ($stmt.type === Syntax.BlockStatement)
+ return _.optSpace;
+
+ if ($stmt.type === Syntax.EmptyStatement)
+ return '';
+
+ return _.newline + _.indent + _.indentUnit;
+ }
+
+ function adoptionSuffix ($stmt) {
+ if ($stmt.type === Syntax.BlockStatement)
+ return _.optSpace;
+
+ return _.newline + _.indent;
+ }
+
+ //Subentities generators
+ function generateVerbatim ($expr, settings) {
+ var verbatim = $expr[extra.verbatim],
+ strVerbatim = typeof verbatim === 'string',
+ precedence = !strVerbatim &&
+ verbatim.precedence !== void 0 ? verbatim.precedence : Precedence.Sequence,
+ parenthesize = precedence < settings.precedence,
+ content = strVerbatim ? verbatim : verbatim.content,
+ chunks = content.split(/\r\n|\n/),
+ chunkCount = chunks.length;
+
+ if (parenthesize)
+ _.js += '(';
+
+ _.js += chunks[0];
+
+ for (var i = 1; i < chunkCount; i++)
+ _.js += _.newline + _.indent + chunks[i];
+
+ if (parenthesize)
+ _.js += ')';
+ }
+
+ function generateFunctionParams ($node) {
+ var $params = $node.params,
+ paramCount = $params.length,
+ lastParamIdx = paramCount - 1,
+ arrowFuncWithoutParentheses = $node.type === Syntax.ArrowFunctionExpression && paramCount === 1 &&
+ $params[0].type === Syntax.Identifier;
+
+ //NOTE: arg => { } case
+ if (arrowFuncWithoutParentheses)
+ _.js += $params[0].name;
+
+ else {
+ _.js += '(';
+
+ for (var i = 0; i < paramCount; ++i) {
+ var $param = $params[i];
+
+ if ($params[i].type === Syntax.Identifier)
+ _.js += $param.name;
+
+ else
+ ExprGen[$param.type]($param, Preset.e4);
+
+ if (i !== lastParamIdx)
+ _.js += ',' + _.optSpace;
+ }
+
+ _.js += ')';
+ }
+ }
+
+ function generateFunctionBody ($node) {
+ var $body = $node.body;
+
+ generateFunctionParams($node);
+
+ if ($node.type === Syntax.ArrowFunctionExpression)
+ _.js += _.optSpace + '=>';
+
+ if ($node.expression) {
+ _.js += _.optSpace;
+
+ var exprJs = exprToJs($body, Preset.e4);
+
+ if (exprJs.charAt(0) === '{')
+ exprJs = '(' + exprJs + ')';
+
+ _.js += exprJs;
+ }
+
+ else {
+ _.js += adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s8);
+ }
+ }
+
+
+ //-------------------------------------------------===------------------------------------------------------
+ // Syntactic entities generation presets
+ //-------------------------------------------------===------------------------------------------------------
+
+ var Preset = {
+ e1: function (allowIn) {
+ return {
+ precedence: Precedence.Assignment,
+ allowIn: allowIn,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ };
+ },
+
+ e2: function (allowIn) {
+ return {
+ precedence: Precedence.LogicalOR,
+ allowIn: allowIn,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ };
+ },
+
+ e3: {
+ precedence: Precedence.Call,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: false
+ },
+
+ e4: {
+ precedence: Precedence.Assignment,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e5: {
+ precedence: Precedence.Sequence,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e6: function (allowUnparenthesizedNew) {
+ return {
+ precedence: Precedence.New,
+ allowIn: true,
+ allowCall: false,
+ allowUnparenthesizedNew: allowUnparenthesizedNew
+ };
+ },
+
+ e7: {
+ precedence: Precedence.Unary,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e8: {
+ precedence: Precedence.Postfix,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e9: {
+ precedence: void 0,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e10: {
+ precedence: Precedence.Call,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+ e11: function (allowCall) {
+ return {
+ precedence: Precedence.Call,
+ allowIn: true,
+ allowCall: allowCall,
+ allowUnparenthesizedNew: false
+ };
+ },
+
+ e12: {
+ precedence: Precedence.Primary,
+ allowIn: false,
+ allowCall: false,
+ allowUnparenthesizedNew: true
+ },
+
+ e13: {
+ precedence: Precedence.Primary,
+ allowIn: true,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+
+ e14: {
+ precedence: Precedence.Sequence,
+ allowIn: false,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ },
+
+
+ e15: function (allowCall) {
+ return {
+ precedence: Precedence.Sequence,
+ allowIn: true,
+ allowCall: allowCall,
+ allowUnparenthesizedNew: true
+ };
+ },
+
+ e16: function (precedence, allowIn) {
+ return {
+ precedence: precedence,
+ allowIn: allowIn,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ };
+ },
+
+ e17: function (allowIn) {
+ return {
+ precedence: Precedence.Call,
+ allowIn: allowIn,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ }
+ },
+
+ e18: function (allowIn) {
+ return {
+ precedence: Precedence.Assignment,
+ allowIn: allowIn,
+ allowCall: true,
+ allowUnparenthesizedNew: true
+ }
+ },
+
+ e19: {
+ precedence: Precedence.Sequence,
+ allowIn: true,
+ allowCall: true,
+ semicolonOptional: false
+ },
+
+ e20: {
+ precedence: Precedence.Await,
+ allowCall: true
+ },
+
+ s1: function (functionBody, semicolonOptional) {
+ return {
+ allowIn: true,
+ functionBody: false,
+ directiveContext: functionBody,
+ semicolonOptional: semicolonOptional
+ };
+ },
+
+ s2: {
+ allowIn: true,
+ functionBody: false,
+ directiveContext: false,
+ semicolonOptional: true
+ },
+
+ s3: function (allowIn) {
+ return {
+ allowIn: allowIn,
+ functionBody: false,
+ directiveContext: false,
+ semicolonOptional: false
+ };
+ },
+
+ s4: function (semicolonOptional) {
+ return {
+ allowIn: true,
+ functionBody: false,
+ directiveContext: false,
+ semicolonOptional: semicolonOptional
+ };
+ },
+
+ s5: function (semicolonOptional) {
+ return {
+ allowIn: true,
+ functionBody: false,
+ directiveContext: true,
+ semicolonOptional: semicolonOptional,
+ };
+ },
+
+ s6: {
+ allowIn: false,
+ functionBody: false,
+ directiveContext: false,
+ semicolonOptional: false
+ },
+
+ s7: {
+ allowIn: true,
+ functionBody: false,
+ directiveContext: false,
+ semicolonOptional: false
+ },
+
+ s8: {
+ allowIn: true,
+ functionBody: true,
+ directiveContext: false,
+ semicolonOptional: false
+ }
+ };
+
+
+ //-------------------------------------------------===-------------------------------------------------------
+ // Expressions
+ //-------------------------------------------------===-------------------------------------------------------
+
+ //Regular expressions
+ var FLOATING_OR_OCTAL_REGEXP = /[.eExX]|^0[0-9]+/,
+ LAST_DECIMAL_DIGIT_REGEXP = /[0-9]$/;
+
+
+ //Common expression generators
+ function generateLogicalOrBinaryExpression ($expr, settings) {
+ var op = $expr.operator,
+ precedence = BinaryPrecedence[$expr.operator],
+ parenthesize = precedence < settings.precedence,
+ allowIn = settings.allowIn || parenthesize,
+ operandGenSettings = Preset.e16(precedence, allowIn),
+ exprJs = exprToJs($expr.left, operandGenSettings);
+
+ parenthesize |= op === 'in' && !allowIn;
+
+ if (parenthesize)
+ _.js += '(';
+
+ // 0x2F = '/'
+ if (exprJs.charCodeAt(exprJs.length - 1) === 0x2F && isIdentifierCh(op.charCodeAt(0)))
+ exprJs = exprJs + _.space + op;
+
+ else
+ exprJs = join(exprJs, op);
+
+ operandGenSettings.precedence++;
+
+ var rightJs = exprToJs($expr.right, operandGenSettings);
+
+ //NOTE: If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
+ if (op === '/' && rightJs.charAt(0) === '/' || op.slice(-1) === '<' && rightJs.slice(0, 3) === '!--')
+ exprJs += _.space + rightJs;
+
+ else
+ exprJs = join(exprJs, rightJs);
+
+ _.js += exprJs;
+
+ if (parenthesize)
+ _.js += ')';
+ }
+
+ function generateArrayPatternOrExpression ($expr) {
+ var $elems = $expr.elements,
+ elemCount = $elems.length;
+
+ if (elemCount) {
+ var lastElemIdx = elemCount - 1,
+ multiline = elemCount > 1,
+ prevIndent = shiftIndent(),
+ itemPrefix = _.newline + _.indent;
+
+ _.js += '[';
+
+ for (var i = 0; i < elemCount; i++) {
+ var $elem = $elems[i];
+
+ if (multiline)
+ _.js += itemPrefix;
+
+ if ($elem)
+ ExprGen[$elem.type]($elem, Preset.e4);
+
+ if (i !== lastElemIdx || !$elem)
+ _.js += ',';
+ }
+
+ _.indent = prevIndent;
+
+ if (multiline)
+ _.js += _.newline + _.indent;
+
+ _.js += ']';
+ }
+
+ else
+ _.js += '[]';
+ }
+
+ function generateGeneratorOrComprehensionExpression ($expr) {
+ //NOTE: GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
+ var $blocks = $expr.blocks,
+ $filter = $expr.filter,
+ isGenerator = $expr.type === Syntax.GeneratorExpression,
+ exprJs = isGenerator ? '(' : '[',
+ bodyJs = exprToJs($expr.body, Preset.e4);
+
+ if ($blocks) {
+ var prevIndent = shiftIndent(),
+ blockCount = $blocks.length;
+
+ for (var i = 0; i < blockCount; ++i) {
+ var blockJs = exprToJs($blocks[i], Preset.e5);
+
+ exprJs = i > 0 ? join(exprJs, blockJs) : (exprJs + blockJs);
+ }
+
+ _.indent = prevIndent;
+ }
+
+ if ($filter) {
+ var filterJs = exprToJs($filter, Preset.e5);
+
+ exprJs = join(exprJs, 'if' + _.optSpace);
+ exprJs = join(exprJs, '(' + filterJs + ')');
+ }
+
+ exprJs = join(exprJs, bodyJs);
+ exprJs += isGenerator ? ')' : ']';
+
+ _.js += exprJs;
+ }
+
+
+ //Expression raw generator dictionary
+ var ExprRawGen = {
+ SequenceExpression: function generateSequenceExpression ($expr, settings) {
+ var $children = $expr.expressions,
+ childrenCount = $children.length,
+ lastChildIdx = childrenCount - 1,
+ parenthesize = Precedence.Sequence < settings.precedence,
+ exprGenSettings = Preset.e1(settings.allowIn || parenthesize);
+
+ if (parenthesize)
+ _.js += '(';
+
+ for (var i = 0; i < childrenCount; i++) {
+ var $child = $children[i];
+
+ ExprGen[$child.type]($child, exprGenSettings);
+
+ if (i !== lastChildIdx)
+ _.js += ',' + _.optSpace;
+ }
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ AssignmentExpression: function generateAssignmentExpression ($expr, settings) {
+ var $left = $expr.left,
+ $right = $expr.right,
+ parenthesize = Precedence.Assignment < settings.precedence,
+ allowIn = settings.allowIn || parenthesize;
+
+ if (parenthesize)
+ _.js += '(';
+
+ ExprGen[$left.type]($left, Preset.e17(allowIn));
+ _.js += _.optSpace + $expr.operator + _.optSpace;
+ ExprGen[$right.type]($right, Preset.e18(allowIn));
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ AssignmentPattern: function generateAssignmentPattern ($node) {
+ var $fakeAssign = {
+ left: $node.left,
+ right: $node.right,
+ operator: '='
+ };
+
+ ExprGen.AssignmentExpression($fakeAssign, Preset.e4);
+ },
+
+ ArrowFunctionExpression: function generateArrowFunctionExpression ($expr, settings) {
+ var parenthesize = Precedence.ArrowFunction < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ if ($expr.async)
+ _.js += 'async ';
+
+ generateFunctionBody($expr);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ AwaitExpression: function generateAwaitExpression ($expr, settings) {
+ var parenthesize = Precedence.Await < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ _.js += $expr.all ? 'await* ' : 'await ';
+
+ ExprGen[$expr.argument.type]($expr.argument, Preset.e20);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ ConditionalExpression: function generateConditionalExpression ($expr, settings) {
+ var $test = $expr.test,
+ $conseq = $expr.consequent,
+ $alt = $expr.alternate,
+ parenthesize = Precedence.Conditional < settings.precedence,
+ allowIn = settings.allowIn || parenthesize,
+ testGenSettings = Preset.e2(allowIn),
+ branchGenSettings = Preset.e1(allowIn);
+
+ if (parenthesize)
+ _.js += '(';
+
+ ExprGen[$test.type]($test, testGenSettings);
+ _.js += _.optSpace + '?' + _.optSpace;
+ ExprGen[$conseq.type]($conseq, branchGenSettings);
+ _.js += _.optSpace + ':' + _.optSpace;
+ ExprGen[$alt.type]($alt, branchGenSettings);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ LogicalExpression: generateLogicalOrBinaryExpression,
+
+ BinaryExpression: generateLogicalOrBinaryExpression,
+
+ CallExpression: function generateCallExpression ($expr, settings) {
+ var $callee = $expr.callee,
+ $args = $expr['arguments'],
+ argCount = $args.length,
+ lastArgIdx = argCount - 1,
+ parenthesize = !settings.allowCall || Precedence.Call < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ ExprGen[$callee.type]($callee, Preset.e3);
+
+ if ($expr.optional)
+ _.js += '?.';
+
+ _.js += '(';
+
+ for (var i = 0; i < argCount; ++i) {
+ var $arg = $args[i];
+
+ ExprGen[$arg.type]($arg, Preset.e4);
+
+ if (i !== lastArgIdx)
+ _.js += ',' + _.optSpace;
+ }
+
+ _.js += ')';
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ NewExpression: function generateNewExpression ($expr, settings) {
+ var $args = $expr['arguments'],
+ parenthesize = Precedence.New < settings.precedence,
+ argCount = $args.length,
+ lastArgIdx = argCount - 1,
+ withCall = !settings.allowUnparenthesizedNew || parentheses || argCount > 0,
+ calleeJs = exprToJs($expr.callee, Preset.e6(!withCall));
+
+ if (parenthesize)
+ _.js += '(';
+
+ _.js += join('new', calleeJs);
+
+ if (withCall) {
+ _.js += '(';
+
+ for (var i = 0; i < argCount; ++i) {
+ var $arg = $args[i];
+
+ ExprGen[$arg.type]($arg, Preset.e4);
+
+ if (i !== lastArgIdx)
+ _.js += ',' + _.optSpace;
+ }
+
+ _.js += ')';
+ }
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ MemberExpression: function generateMemberExpression ($expr, settings) {
+ var $obj = $expr.object,
+ $prop = $expr.property,
+ parenthesize = Precedence.Member < settings.precedence,
+ isNumObj = !$expr.computed && $obj.type === Syntax.Literal && typeof $obj.value === 'number';
+
+ if (parenthesize)
+ _.js += '(';
+
+ if (isNumObj) {
+
+ //NOTE: When the following conditions are all true:
+ // 1. No floating point
+ // 2. Don't have exponents
+ // 3. The last character is a decimal digit
+ // 4. Not hexadecimal OR octal number literal
+ // then we should add a floating point.
+
+ var numJs = exprToJs($obj, Preset.e11(settings.allowCall)),
+ withPoint = LAST_DECIMAL_DIGIT_REGEXP.test(numJs) && !FLOATING_OR_OCTAL_REGEXP.test(numJs);
+
+ _.js += withPoint ? (numJs + '.') : numJs;
+ }
+
+ else
+ ExprGen[$obj.type]($obj, Preset.e11(settings.allowCall));
+
+ if ($expr.computed) {
+ if ($expr.optional)
+ _.js += '?.';
+
+ _.js += '[';
+ ExprGen[$prop.type]($prop, Preset.e15(settings.allowCall));
+ _.js += ']';
+ }
+
+ else
+ _.js += ($expr.optional ? '?.' : '.') + $prop.name;
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ UnaryExpression: function generateUnaryExpression ($expr, settings) {
+ var parenthesize = Precedence.Unary < settings.precedence,
+ op = $expr.operator,
+ argJs = exprToJs($expr.argument, Preset.e7);
+
+ if (parenthesize)
+ _.js += '(';
+
+ //NOTE: delete, void, typeof
+ // get `typeof []`, not `typeof[]`
+ if (_.optSpace === '' || op.length > 2)
+ _.js += join(op, argJs);
+
+ else {
+ _.js += op;
+
+ //NOTE: Prevent inserting spaces between operator and argument if it is unnecessary
+ // like, `!cond`
+ var leftCp = op.charCodeAt(op.length - 1),
+ rightCp = argJs.charCodeAt(0);
+
+ // 0x2B = '+', 0x2D = '-'
+ if (leftCp === rightCp && (leftCp === 0x2B || leftCp === 0x2D) ||
+ isIdentifierCh(leftCp) && isIdentifierCh(rightCp)) {
+ _.js += _.space;
+ }
+
+ _.js += argJs;
+ }
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ YieldExpression: function generateYieldExpression ($expr, settings) {
+ var $arg = $expr.argument,
+ js = $expr.delegate ? 'yield*' : 'yield',
+ parenthesize = Precedence.Yield < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ if ($arg) {
+ var argJs = exprToJs($arg, Preset.e4);
+
+ js = join(js, argJs);
+ }
+
+ _.js += js;
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ UpdateExpression: function generateUpdateExpression ($expr, settings) {
+ var $arg = $expr.argument,
+ $op = $expr.operator,
+ prefix = $expr.prefix,
+ precedence = prefix ? Precedence.Unary : Precedence.Postfix,
+ parenthesize = precedence < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ if (prefix) {
+ _.js += $op;
+ ExprGen[$arg.type]($arg, Preset.e8);
+
+ }
+
+ else {
+ ExprGen[$arg.type]($arg, Preset.e8);
+ _.js += $op;
+ }
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ FunctionExpression: function generateFunctionExpression ($expr) {
+ var isGenerator = !!$expr.generator;
+
+ if ($expr.async)
+ _.js += 'async ';
+
+ _.js += isGenerator ? 'function*' : 'function';
+
+ if ($expr.id) {
+ _.js += isGenerator ? _.optSpace : _.space;
+ _.js += $expr.id.name;
+ }
+ else
+ _.js += _.optSpace;
+
+ generateFunctionBody($expr);
+ },
+
+ ExportBatchSpecifier: function generateExportBatchSpecifier () {
+ _.js += '*';
+ },
+
+ ArrayPattern: generateArrayPatternOrExpression,
+
+ ArrayExpression: generateArrayPatternOrExpression,
+
+ ClassExpression: function generateClassExpression ($expr) {
+ var $id = $expr.id,
+ $super = $expr.superClass,
+ $body = $expr.body,
+ exprJs = 'class';
+
+ if ($id) {
+ var idJs = exprToJs($id, Preset.e9);
+
+ exprJs = join(exprJs, idJs);
+ }
+
+ if ($super) {
+ var superJs = exprToJs($super, Preset.e4);
+
+ superJs = join('extends', superJs);
+ exprJs = join(exprJs, superJs);
+ }
+
+ _.js += exprJs + _.optSpace;
+ StmtGen[$body.type]($body, Preset.s2);
+ },
+
+ MetaProperty: function generateMetaProperty ($expr, settings) {
+ var $meta = $expr.meta,
+ $property = $expr.property,
+ parenthesize = Precedence.Member < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ _.js += (typeof $meta === "string" ? $meta : $meta.name) +
+ '.' + (typeof $property === "string" ? $property : $property.name);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ MethodDefinition: function generateMethodDefinition ($expr) {
+ var exprJs = $expr['static'] ? 'static' + _.optSpace : '',
+ keyJs = exprToJs($expr.key, Preset.e5);
+
+ if ($expr.computed)
+ keyJs = '[' + keyJs + ']';
+
+ if ($expr.kind === 'get' || $expr.kind === 'set') {
+ keyJs = join($expr.kind, keyJs);
+ _.js += join(exprJs, keyJs);
+ }
+
+ else {
+ if ($expr.value.generator)
+ _.js += exprJs + '*' + keyJs;
+ else if ($expr.value.async)
+ _.js += exprJs + 'async ' + keyJs;
+ else
+ _.js += join(exprJs, keyJs);
+ }
+
+ generateFunctionBody($expr.value);
+ },
+
+ Property: function generateProperty ($expr) {
+ var $val = $expr.value,
+ $kind = $expr.kind,
+ keyJs = exprToJs($expr.key, Preset.e4);
+
+ if ($expr.computed)
+ keyJs = '[' + keyJs + ']';
+
+ if ($kind === 'get' || $kind === 'set') {
+ _.js += $kind + _.space + keyJs;
+ generateFunctionBody($val);
+ }
+
+ else {
+ if ($expr.shorthand)
+ _.js += keyJs;
+
+ else if ($expr.method) {
+ if ($val.generator)
+ keyJs = '*' + keyJs;
+ else if ($val.async)
+ keyJs = 'async ' + keyJs;
+
+ _.js += keyJs;
+ generateFunctionBody($val)
+ }
+
+ else {
+ _.js += keyJs + ':' + _.optSpace;
+ ExprGen[$val.type]($val, Preset.e4);
+ }
+ }
+ },
+
+ ObjectExpression: function generateObjectExpression ($expr) {
+ var $props = $expr.properties,
+ propCount = $props.length;
+
+ if (propCount) {
+ var lastPropIdx = propCount - 1,
+ prevIndent = shiftIndent();
+
+ _.js += '{';
+
+ for (var i = 0; i < propCount; i++) {
+ var $prop = $props[i],
+ propType = $prop.type || Syntax.Property;
+
+ _.js += _.newline + _.indent;
+ ExprGen[propType]($prop, Preset.e5);
+
+ if (i !== lastPropIdx)
+ _.js += ',';
+ }
+
+ _.indent = prevIndent;
+ _.js += _.newline + _.indent + '}';
+ }
+
+ else
+ _.js += '{}';
+ },
+
+ ObjectPattern: function generateObjectPattern ($expr) {
+ var $props = $expr.properties,
+ propCount = $props.length;
+
+ if (propCount) {
+ var lastPropIdx = propCount - 1,
+ multiline = false;
+
+ if (propCount === 1 && $props[0].value)
+ multiline = $props[0].value.type !== Syntax.Identifier;
+
+ else {
+ for (var i = 0; i < propCount; i++) {
+ if (!$props[i].shorthand) {
+ multiline = true;
+ break;
+ }
+ }
+ }
+
+ _.js += multiline ? ('{' + _.newline) : '{';
+
+ var prevIndent = shiftIndent(),
+ propSuffix = ',' + (multiline ? _.newline : _.optSpace);
+
+ for (var i = 0; i < propCount; i++) {
+ var $prop = $props[i];
+
+ if (multiline)
+ _.js += _.indent;
+
+ ExprGen[$prop.type]($prop, Preset.e5);
+
+ if (i !== lastPropIdx)
+ _.js += propSuffix;
+ }
+
+ _.indent = prevIndent;
+ _.js += multiline ? (_.newline + _.indent + '}') : '}';
+ }
+ else
+ _.js += '{}';
+ },
+
+ ThisExpression: function generateThisExpression () {
+ _.js += 'this';
+ },
+
+ Identifier: function generateIdentifier ($expr, precedence, flag) {
+ _.js += $expr.name;
+ },
+
+ ImportExpression: function generateImportExpression ($expr, settings) {
+ var parenthesize = Precedence.Call < settings.precedence;
+ var $source = $expr.source;
+
+ if (parenthesize)
+ _.js += '(';
+
+ _.js += 'import(';
+
+ ExprGen[$source.type]($source, Preset.e4);
+
+ _.js += ')';
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ ImportSpecifier: function generateImportSpecifier ($expr) {
+ _.js += $expr.imported.name;
+
+ if ($expr.local)
+ _.js += _.space + 'as' + _.space + $expr.local.name;
+ },
+
+ ExportSpecifier: function generateImportOrExportSpecifier ($expr) {
+ _.js += $expr.local.name;
+
+ if ($expr.exported)
+ _.js += _.space + 'as' + _.space + $expr.exported.name;
+ },
+
+ ChainExpression: function generateChainExpression ($expr, settings) {
+ var parenthesize = Precedence.OptionalChaining < settings.precedence;
+ var $expression = $expr.expression;
+
+ settings = settings || {};
+
+ var newSettings = {
+ precedence: Precedence.OptionalChaining,
+ allowIn: settings.allowIn ,
+ allowCall: settings.allowCall,
+
+ allowUnparenthesizedNew: settings.allowUnparenthesizedNew
+ }
+
+ if (parenthesize) {
+ newSettings.allowCall = true;
+ _.js += '(';
+ }
+
+ ExprGen[$expression.type]($expression, newSettings);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ Literal: function generateLiteral ($expr) {
+ if (extra.raw && $expr.raw !== void 0)
+ _.js += $expr.raw;
+
+ else if ($expr.value === null)
+ _.js += 'null';
+
+ else {
+ var valueType = typeof $expr.value;
+
+ if (valueType === 'string')
+ _.js += escapeString($expr.value);
+
+ else if (valueType === 'number')
+ _.js += generateNumber($expr.value);
+
+ else if (valueType === 'boolean')
+ _.js += $expr.value ? 'true' : 'false';
+
+ else
+ _.js += generateRegExp($expr.value);
+ }
+ },
+
+ GeneratorExpression: generateGeneratorOrComprehensionExpression,
+
+ ComprehensionExpression: generateGeneratorOrComprehensionExpression,
+
+ ComprehensionBlock: function generateComprehensionBlock ($expr) {
+ var $left = $expr.left,
+ leftJs = void 0,
+ rightJs = exprToJs($expr.right, Preset.e5);
+
+ if ($left.type === Syntax.VariableDeclaration)
+ leftJs = $left.kind + _.space + stmtToJs($left.declarations[0], Preset.s6);
+
+ else
+ leftJs = exprToJs($left, Preset.e10);
+
+ leftJs = join(leftJs, $expr.of ? 'of' : 'in');
+
+ _.js += 'for' + _.optSpace + '(' + join(leftJs, rightJs) + ')';
+ },
+
+ RestElement: function generateRestElement ($node) {
+ _.js += '...' + $node.argument.name;
+ },
+
+ SpreadElement: function generateSpreadElement ($expr) {
+ var $arg = $expr.argument;
+
+ _.js += '...';
+ ExprGen[$arg.type]($arg, Preset.e4);
+ },
+
+ TaggedTemplateExpression: function generateTaggedTemplateExpression ($expr, settings) {
+ var $tag = $expr.tag,
+ $quasi = $expr.quasi,
+ parenthesize = Precedence.TaggedTemplate < settings.precedence;
+
+ if (parenthesize)
+ _.js += '(';
+
+ ExprGen[$tag.type]($tag, Preset.e11(settings.allowCall));
+ ExprGen[$quasi.type]($quasi, Preset.e12);
+
+ if (parenthesize)
+ _.js += ')';
+ },
+
+ TemplateElement: function generateTemplateElement ($expr) {
+ //NOTE: Don't use "cooked". Since tagged template can use raw template
+ // representation. So if we do so, it breaks the script semantics.
+ _.js += $expr.value.raw;
+ },
+
+ TemplateLiteral: function generateTemplateLiteral ($expr) {
+ var $quasis = $expr.quasis,
+ $childExprs = $expr.expressions,
+ quasiCount = $quasis.length,
+ lastQuasiIdx = quasiCount - 1;
+
+ _.js += '`';
+
+ for (var i = 0; i < quasiCount; ++i) {
+ var $quasi = $quasis[i];
+
+ ExprGen[$quasi.type]($quasi, Preset.e13);
+
+ if (i !== lastQuasiIdx) {
+ var $childExpr = $childExprs[i];
+
+ _.js += '${' + _.optSpace;
+ ExprGen[$childExpr.type]($childExpr, Preset.e5);
+ _.js += _.optSpace + '}';
+ }
+ }
+
+ _.js += '`';
+ },
+
+ Super: function generateSuper () {
+ _.js += 'super';
+ }
+ };
+
+
+ //-------------------------------------------------===------------------------------------------------------
+ // Statements
+ //-------------------------------------------------===------------------------------------------------------
+
+
+ //Regular expressions
+ var EXPR_STMT_UNALLOWED_EXPR_REGEXP = /^{|^class(?:\s|{)|^(async )?function(?:\s|\*|\()/;
+
+
+ //Common statement generators
+ function generateTryStatementHandlers (stmtJs, $finalizer, handlers) {
+ var handlerCount = handlers.length,
+ lastHandlerIdx = handlerCount - 1;
+
+ for (var i = 0; i < handlerCount; ++i) {
+ var handlerJs = stmtToJs(handlers[i], Preset.s7);
+
+ stmtJs = join(stmtJs, handlerJs);
+
+ if ($finalizer || i !== lastHandlerIdx)
+ stmtJs += adoptionSuffix(handlers[i].body);
+ }
+
+ return stmtJs;
+ }
+
+ function generateForStatementIterator ($op, $stmt, settings) {
+ var $body = $stmt.body,
+ $left = $stmt.left,
+ bodySemicolonOptional = !semicolons && settings.semicolonOptional,
+ prevIndent1 = shiftIndent(),
+ awaitStr = $stmt.await ? ' await' : '',
+ stmtJs = 'for' + awaitStr + _.optSpace + '(';
+
+ if ($left.type === Syntax.VariableDeclaration) {
+ var prevIndent2 = shiftIndent();
+
+ stmtJs += $left.kind + _.space + stmtToJs($left.declarations[0], Preset.s6);
+ _.indent = prevIndent2;
+ }
+
+ else
+ stmtJs += exprToJs($left, Preset.e10);
+
+ stmtJs = join(stmtJs, $op);
+
+ var rightJs = exprToJs($stmt.right, Preset.e4);
+
+ stmtJs = join(stmtJs, rightJs) + ')';
+
+ _.indent = prevIndent1;
+
+ _.js += stmtJs + adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s4(bodySemicolonOptional));
+ }
+
+
+ //Statement generator dictionary
+ var StmtRawGen = {
+ BlockStatement: function generateBlockStatement ($stmt, settings) {
+ var $body = $stmt.body,
+ len = $body.length,
+ lastIdx = len - 1,
+ prevIndent = shiftIndent();
+
+ _.js += '{' + _.newline;
+
+ for (var i = 0; i < len; i++) {
+ var $item = $body[i];
+
+ _.js += _.indent;
+ StmtGen[$item.type]($item, Preset.s1(settings.functionBody, i === lastIdx));
+ _.js += _.newline;
+ }
+
+ _.indent = prevIndent;
+ _.js += _.indent + '}';
+ },
+
+ BreakStatement: function generateBreakStatement ($stmt, settings) {
+ if ($stmt.label)
+ _.js += 'break ' + $stmt.label.name;
+
+ else
+ _.js += 'break';
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ ContinueStatement: function generateContinueStatement ($stmt, settings) {
+ if ($stmt.label)
+ _.js += 'continue ' + $stmt.label.name;
+
+ else
+ _.js += 'continue';
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ ClassBody: function generateClassBody ($stmt) {
+ var $body = $stmt.body,
+ itemCount = $body.length,
+ lastItemIdx = itemCount - 1,
+ prevIndent = shiftIndent();
+
+ _.js += '{' + _.newline;
+
+ for (var i = 0; i < itemCount; i++) {
+ var $item = $body[i],
+ itemType = $item.type || Syntax.Property;
+
+ _.js += _.indent;
+ ExprGen[itemType]($item, Preset.e5);
+
+ if (i !== lastItemIdx)
+ _.js += _.newline;
+ }
+
+ _.indent = prevIndent;
+ _.js += _.newline + _.indent + '}';
+ },
+
+ ClassDeclaration: function generateClassDeclaration ($stmt) {
+ var $body = $stmt.body,
+ $super = $stmt.superClass,
+ js = 'class ' + $stmt.id.name;
+
+ if ($super) {
+ var superJs = exprToJs($super, Preset.e4);
+
+ js += _.space + join('extends', superJs);
+ }
+
+ _.js += js + _.optSpace;
+ StmtGen[$body.type]($body, Preset.s2);
+ },
+
+ DirectiveStatement: function generateDirectiveStatement ($stmt, settings) {
+ if (extra.raw && $stmt.raw)
+ _.js += $stmt.raw;
+
+ else
+ _.js += escapeDirective($stmt.directive);
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ DoWhileStatement: function generateDoWhileStatement ($stmt, settings) {
+ var $body = $stmt.body,
+ $test = $stmt.test,
+ bodyJs = adoptionPrefix($body) +
+ stmtToJs($body, Preset.s7) +
+ adoptionSuffix($body);
+
+ //NOTE: Because `do 42 while (cond)` is Syntax Error. We need semicolon.
+ var stmtJs = join('do', bodyJs);
+
+ _.js += join(stmtJs, 'while' + _.optSpace + '(');
+ ExprGen[$test.type]($test, Preset.e5);
+ _.js += ')';
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ CatchClause: function generateCatchClause ($stmt) {
+ var $param = $stmt.param,
+ $guard = $stmt.guard,
+ $body = $stmt.body,
+ prevIndent = shiftIndent();
+
+ _.js += 'catch' + _.optSpace;
+
+ if ($param) {
+ _.js += '(';
+ ExprGen[$param.type]($param, Preset.e5);
+ }
+
+ if ($guard) {
+ _.js += ' if ';
+ ExprGen[$guard.type]($guard, Preset.e5);
+ }
+
+ _.indent = prevIndent;
+ if ($param) {
+ _.js += ')';
+ }
+
+ _.js += adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s7);
+ },
+
+ DebuggerStatement: function generateDebuggerStatement ($stmt, settings) {
+ _.js += 'debugger';
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ EmptyStatement: function generateEmptyStatement () {
+ _.js += ';';
+ },
+
+ ExportAllDeclaration: function ($stmt, settings) {
+ StmtRawGen.ExportDeclaration($stmt, settings, true);
+ },
+
+ ExportDeclaration: function generateExportDeclaration ($stmt, settings, exportAll) {
+ var $specs = $stmt.specifiers,
+ $decl = $stmt.declaration,
+ withSemicolon = semicolons || !settings.semicolonOptional;
+
+ // export default AssignmentExpression[In] ;
+ if ($stmt['default']) {
+ var declJs = exprToJs($decl, Preset.e4);
+
+ _.js += join('export default', declJs);
+
+ if (withSemicolon)
+ _.js += ';';
+ }
+
+ // export * FromClause ;
+ // export ExportClause[NoReference] FromClause ;
+ // export ExportClause ;
+ else if ($specs || exportAll) {
+ var stmtJs = 'export';
+
+ if (exportAll)
+ stmtJs += _.optSpace + '*';
+
+ else if ($specs.length === 0)
+ stmtJs += _.optSpace + '{' + _.optSpace + '}';
+
+ else if ($specs[0].type === Syntax.ExportBatchSpecifier) {
+ var specJs = exprToJs($specs[0], Preset.e5);
+
+ stmtJs = join(stmtJs, specJs);
+ }
+
+ else {
+ var prevIndent = shiftIndent(),
+ specCount = $specs.length,
+ lastSpecIdx = specCount - 1;
+
+ stmtJs += _.optSpace + '{';
+
+ for (var i = 0; i < specCount; ++i) {
+ stmtJs += _.newline + _.indent;
+ stmtJs += exprToJs($specs[i], Preset.e5);
+
+ if (i !== lastSpecIdx)
+ stmtJs += ',';
+ }
+
+ _.indent = prevIndent;
+ stmtJs += _.newline + _.indent + '}';
+ }
+
+ if ($stmt.source) {
+ _.js += join(stmtJs, 'from' + _.optSpace);
+ ExprGen.Literal($stmt.source);
+ }
+
+ else
+ _.js += stmtJs;
+
+ if (withSemicolon)
+ _.js += ';';
+ }
+
+ // export VariableStatement
+ // export Declaration[Default]
+ else if ($decl) {
+ var declJs = stmtToJs($decl, Preset.s4(!withSemicolon));
+
+ _.js += join('export', declJs);
+ }
+ },
+
+ ExportNamedDeclaration: function ($stmt, settings) {
+ StmtRawGen.ExportDeclaration($stmt, settings);
+ },
+
+ ExpressionStatement: function generateExpressionStatement ($stmt, settings) {
+ var exprJs = exprToJs($stmt.expression, Preset.e5),
+ parenthesize = EXPR_STMT_UNALLOWED_EXPR_REGEXP.test(exprJs) ||
+ (directive &&
+ settings.directiveContext &&
+ $stmt.expression.type === Syntax.Literal &&
+ typeof $stmt.expression.value === 'string');
+
+ //NOTE: '{', 'function', 'class' are not allowed in expression statement.
+ // Therefore, they should be parenthesized.
+ if (parenthesize)
+ _.js += '(' + exprJs + ')';
+
+ else
+ _.js += exprJs;
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ ImportDeclaration: function generateImportDeclaration ($stmt, settings) {
+ var $specs = $stmt.specifiers,
+ stmtJs = 'import',
+ specCount = $specs.length;
+
+ //NOTE: If no ImportClause is present,
+ // this should be `import ModuleSpecifier` so skip `from`
+ // ModuleSpecifier is StringLiteral.
+ if (specCount) {
+ var hasBinding = !!$specs[0]['default'],
+ firstNamedIdx = hasBinding ? 1 : 0,
+ lastSpecIdx = specCount - 1;
+
+ // ImportedBinding
+ if (hasBinding)
+ stmtJs = join(stmtJs, $specs[0].id.name);
+
+ // NamedImports
+ if (firstNamedIdx < specCount) {
+ if (hasBinding)
+ stmtJs += ',';
+
+ stmtJs += _.optSpace + '{';
+
+ // import { ... } from "...";
+ if (firstNamedIdx === lastSpecIdx)
+ stmtJs += _.optSpace + exprToJs($specs[firstNamedIdx], Preset.e5) + _.optSpace;
+
+ else {
+ var prevIndent = shiftIndent();
+
+ // import {
+ // ...,
+ // ...,
+ // } from "...";
+ for (var i = firstNamedIdx; i < specCount; i++) {
+ stmtJs += _.newline + _.indent + exprToJs($specs[i], Preset.e5);
+
+ if (i !== lastSpecIdx)
+ stmtJs += ',';
+ }
+
+ _.indent = prevIndent;
+ stmtJs += _.newline + _.indent;
+ }
+
+ stmtJs += '}' + _.optSpace;
+ }
+
+ stmtJs = join(stmtJs, 'from')
+ }
+
+ _.js += stmtJs + _.optSpace;
+ ExprGen.Literal($stmt.source);
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ VariableDeclarator: function generateVariableDeclarator ($stmt, settings) {
+ var $id = $stmt.id,
+ $init = $stmt.init,
+ genSettings = Preset.e1(settings.allowIn);
+
+ if ($init) {
+ ExprGen[$id.type]($id, genSettings);
+ _.js += _.optSpace + '=' + _.optSpace;
+ ExprGen[$init.type]($init, genSettings);
+ }
+
+ else {
+ if ($id.type === Syntax.Identifier)
+ _.js += $id.name;
+
+ else
+ ExprGen[$id.type]($id, genSettings);
+ }
+ },
+
+ VariableDeclaration: function generateVariableDeclaration ($stmt, settings) {
+ var $decls = $stmt.declarations,
+ len = $decls.length,
+ prevIndent = len > 1 ? shiftIndent() : _.indent,
+ declGenSettings = Preset.s3(settings.allowIn);
+
+ _.js += $stmt.kind;
+
+ for (var i = 0; i < len; i++) {
+ var $decl = $decls[i];
+
+ _.js += i === 0 ? _.space : (',' + _.optSpace);
+ StmtGen[$decl.type]($decl, declGenSettings);
+ }
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+
+ _.indent = prevIndent;
+ },
+
+ ThrowStatement: function generateThrowStatement ($stmt, settings) {
+ var argJs = exprToJs($stmt.argument, Preset.e5);
+
+ _.js += join('throw', argJs);
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ TryStatement: function generateTryStatement ($stmt) {
+ var $block = $stmt.block,
+ $finalizer = $stmt.finalizer,
+ stmtJs = 'try' +
+ adoptionPrefix($block) +
+ stmtToJs($block, Preset.s7) +
+ adoptionSuffix($block);
+
+ var $handlers = $stmt.handlers || $stmt.guardedHandlers;
+
+ if ($handlers)
+ stmtJs = generateTryStatementHandlers(stmtJs, $finalizer, $handlers);
+
+ if ($stmt.handler) {
+ $handlers = isArray($stmt.handler) ? $stmt.handler : [$stmt.handler];
+ stmtJs = generateTryStatementHandlers(stmtJs, $finalizer, $handlers);
+ }
+
+ if ($finalizer) {
+ stmtJs = join(stmtJs, 'finally' + adoptionPrefix($finalizer));
+ stmtJs += stmtToJs($finalizer, Preset.s7);
+ }
+
+ _.js += stmtJs;
+ },
+
+ SwitchStatement: function generateSwitchStatement ($stmt) {
+ var $cases = $stmt.cases,
+ $discr = $stmt.discriminant,
+ prevIndent = shiftIndent();
+
+ _.js += 'switch' + _.optSpace + '(';
+ ExprGen[$discr.type]($discr, Preset.e5);
+ _.js += ')' + _.optSpace + '{' + _.newline;
+ _.indent = prevIndent;
+
+ if ($cases) {
+ var caseCount = $cases.length,
+ lastCaseIdx = caseCount - 1;
+
+ for (var i = 0; i < caseCount; i++) {
+ var $case = $cases[i];
+
+ _.js += _.indent;
+ StmtGen[$case.type]($case, Preset.s4(i === lastCaseIdx));
+ _.js += _.newline;
+ }
+ }
+
+ _.js += _.indent + '}';
+ },
+
+ SwitchCase: function generateSwitchCase ($stmt, settings) {
+ var $conseqs = $stmt.consequent,
+ $firstConseq = $conseqs[0],
+ $test = $stmt.test,
+ i = 0,
+ conseqSemicolonOptional = !semicolons && settings.semicolonOptional,
+ conseqCount = $conseqs.length,
+ lastConseqIdx = conseqCount - 1,
+ prevIndent = shiftIndent();
+
+ if ($test) {
+ var testJs = exprToJs($test, Preset.e5);
+
+ _.js += join('case', testJs) + ':';
+ }
+
+ else
+ _.js += 'default:';
+
+
+ if (conseqCount && $firstConseq.type === Syntax.BlockStatement) {
+ i++;
+ _.js += adoptionPrefix($firstConseq);
+ StmtGen[$firstConseq.type]($firstConseq, Preset.s7);
+ }
+
+ for (; i < conseqCount; i++) {
+ var $conseq = $conseqs[i],
+ semicolonOptional = i === lastConseqIdx && conseqSemicolonOptional;
+
+ _.js += _.newline + _.indent;
+ StmtGen[$conseq.type]($conseq, Preset.s4(semicolonOptional));
+ }
+
+ _.indent = prevIndent;
+ },
+
+ IfStatement: function generateIfStatement ($stmt, settings) {
+ var $conseq = $stmt.consequent,
+ $test = $stmt.test,
+ prevIndent = shiftIndent(),
+ semicolonOptional = !semicolons && settings.semicolonOptional;
+
+ _.js += 'if' + _.optSpace + '(';
+ ExprGen[$test.type]($test, Preset.e5);
+ _.js += ')';
+ _.indent = prevIndent;
+ _.js += adoptionPrefix($conseq);
+
+ if ($stmt.alternate) {
+ var conseq = stmtToJs($conseq, Preset.s7) + adoptionSuffix($conseq),
+ alt = stmtToJs($stmt.alternate, Preset.s4(semicolonOptional));
+
+ if ($stmt.alternate.type === Syntax.IfStatement)
+ alt = 'else ' + alt;
+
+ else
+ alt = join('else', adoptionPrefix($stmt.alternate) + alt);
+
+ _.js += join(conseq, alt);
+ }
+
+ else
+ StmtGen[$conseq.type]($conseq, Preset.s4(semicolonOptional));
+ },
+
+ ForStatement: function generateForStatement ($stmt, settings) {
+ var $init = $stmt.init,
+ $test = $stmt.test,
+ $body = $stmt.body,
+ $update = $stmt.update,
+ bodySemicolonOptional = !semicolons && settings.semicolonOptional,
+ prevIndent = shiftIndent();
+
+ _.js += 'for' + _.optSpace + '(';
+
+ if ($init) {
+ if ($init.type === Syntax.VariableDeclaration)
+ StmtGen[$init.type]($init, Preset.s6);
+
+ else {
+ ExprGen[$init.type]($init, Preset.e14);
+ _.js += ';';
+ }
+ }
+
+ else
+ _.js += ';';
+
+ if ($test) {
+ _.js += _.optSpace;
+ ExprGen[$test.type]($test, Preset.e5);
+ }
+
+ _.js += ';';
+
+ if ($update) {
+ _.js += _.optSpace;
+ ExprGen[$update.type]($update, Preset.e5);
+ }
+
+ _.js += ')';
+ _.indent = prevIndent;
+ _.js += adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s4(bodySemicolonOptional));
+ },
+
+ ForInStatement: function generateForInStatement ($stmt, settings) {
+ generateForStatementIterator('in', $stmt, settings);
+ },
+
+ ForOfStatement: function generateForOfStatement ($stmt, settings) {
+ generateForStatementIterator('of', $stmt, settings);
+ },
+
+ LabeledStatement: function generateLabeledStatement ($stmt, settings) {
+ var $body = $stmt.body,
+ bodySemicolonOptional = !semicolons && settings.semicolonOptional,
+ prevIndent = _.indent;
+
+ _.js += $stmt.label.name + ':' + adoptionPrefix($body);
+
+ if ($body.type !== Syntax.BlockStatement)
+ prevIndent = shiftIndent();
+
+ StmtGen[$body.type]($body, Preset.s4(bodySemicolonOptional));
+ _.indent = prevIndent;
+ },
+
+ ModuleDeclaration: function generateModuleDeclaration ($stmt, settings) {
+ _.js += 'module' + _.space + $stmt.id.name + _.space + 'from' + _.optSpace;
+
+ ExprGen.Literal($stmt.source);
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ Program: function generateProgram ($stmt) {
+ var $body = $stmt.body,
+ len = $body.length,
+ lastIdx = len - 1;
+
+ if (safeConcatenation && len > 0)
+ _.js += '\n';
+
+ for (var i = 0; i < len; i++) {
+ var $item = $body[i];
+
+ _.js += _.indent;
+ StmtGen[$item.type]($item, Preset.s5(!safeConcatenation && i === lastIdx));
+
+ if (i !== lastIdx)
+ _.js += _.newline;
+ }
+ },
+
+ FunctionDeclaration: function generateFunctionDeclaration ($stmt) {
+ var isGenerator = !!$stmt.generator;
+
+ if ($stmt.async)
+ _.js += 'async ';
+
+ _.js += isGenerator ? ('function*' + _.optSpace) : ('function' + _.space );
+ _.js += $stmt.id.name;
+ generateFunctionBody($stmt);
+ },
+
+ ReturnStatement: function generateReturnStatement ($stmt, settings) {
+ var $arg = $stmt.argument;
+
+ if ($arg) {
+ var argJs = exprToJs($arg, Preset.e5);
+
+ _.js += join('return', argJs);
+ }
+
+ else
+ _.js += 'return';
+
+ if (semicolons || !settings.semicolonOptional)
+ _.js += ';';
+ },
+
+ WhileStatement: function generateWhileStatement ($stmt, settings) {
+ var $body = $stmt.body,
+ $test = $stmt.test,
+ bodySemicolonOptional = !semicolons && settings.semicolonOptional,
+ prevIndent = shiftIndent();
+
+ _.js += 'while' + _.optSpace + '(';
+ ExprGen[$test.type]($test, Preset.e5);
+ _.js += ')';
+ _.indent = prevIndent;
+
+ _.js += adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s4(bodySemicolonOptional));
+ },
+
+ WithStatement: function generateWithStatement ($stmt, settings) {
+ var $body = $stmt.body,
+ $obj = $stmt.object,
+ bodySemicolonOptional = !semicolons && settings.semicolonOptional,
+ prevIndent = shiftIndent();
+
+ _.js += 'with' + _.optSpace + '(';
+ ExprGen[$obj.type]($obj, Preset.e5);
+ _.js += ')';
+ _.indent = prevIndent;
+ _.js += adoptionPrefix($body);
+ StmtGen[$body.type]($body, Preset.s4(bodySemicolonOptional));
+ }
+ };
+
+ function generateStatement ($stmt, option) {
+ StmtGen[$stmt.type]($stmt, option);
+ }
+
+ //CodeGen
+ //-----------------------------------------------------------------------------------
+ function exprToJs ($expr, settings) {
+ var savedJs = _.js;
+ _.js = '';
+
+ // ExprGen[$expr.type]($expr, settings);
+ // Modified for Corrosion
+ if (typeof ExprGen[$expr.type] == 'function') ExprGen[$expr.type]($expr, settings);
+
+ var src = _.js;
+ _.js = savedJs;
+
+ return src;
+ }
+
+ function stmtToJs ($stmt, settings) {
+ var savedJs = _.js;
+ _.js = '';
+
+ StmtGen[$stmt.type]($stmt, settings);
+
+ var src = _.js;
+ _.js = savedJs;
+
+ return src;
+ }
+
+ function run ($node) {
+ _.js = '';
+
+ if (StmtGen[$node.type])
+ StmtGen[$node.type]($node, Preset.s7);
+
+ else
+ ExprGen[$node.type]($node, Preset.e19);
+
+ return _.js;
+ }
+
+ function wrapExprGen (gen) {
+ return function ($expr, settings) {
+ if (extra.verbatim && $expr.hasOwnProperty(extra.verbatim))
+ generateVerbatim($expr, settings);
+
+ else
+ gen($expr, settings);
+ }
+ }
+
+ function createExprGenWithExtras () {
+ var gens = {};
+
+ for (var key in ExprRawGen) {
+ if (ExprRawGen.hasOwnProperty(key))
+ gens[key] = wrapExprGen(ExprRawGen[key]);
+ }
+
+ return gens;
+ }
+
+
+ //Strings
+ var _ = {
+ js: '',
+ newline: '\n',
+ optSpace: ' ',
+ space: ' ',
+ indentUnit: ' ',
+ indent: ''
+ };
+
+
+ //Generators
+ var ExprGen = void 0,
+ StmtGen = StmtRawGen;
+
+
+ exports.generate = function ($node, options) {
+ var defaultOptions = getDefaultOptions(), result, pair;
+
+ if (options != null) {
+ //NOTE: Obsolete options
+ //
+ // `options.indent`
+ // `options.base`
+ //
+ // Instead of them, we can use `option.format.indent`.
+ if (typeof options.indent === 'string') {
+ defaultOptions.format.indent.style = options.indent;
+ }
+ if (typeof options.base === 'number') {
+ defaultOptions.format.indent.base = options.base;
+ }
+ options = updateDeeply(defaultOptions, options);
+ _.indentUnit = options.format.indent.style;
+ if (typeof options.base === 'string') {
+ _.indent = options.base;
+ }
+ else {
+ _.indent = stringRepeat(_.indentUnit, options.format.indent.base);
+ }
+ }
+ else {
+ options = defaultOptions;
+ _.indentUnit = options.format.indent.style;
+ _.indent = stringRepeat(_.indentUnit, options.format.indent.base);
+ }
+ json = options.format.json;
+ renumber = options.format.renumber;
+ hexadecimal = json ? false : options.format.hexadecimal;
+ quotes = json ? 'double' : options.format.quotes;
+ escapeless = options.format.escapeless;
+
+ _.newline = options.format.newline;
+ _.optSpace = options.format.space;
+
+ if (options.format.compact)
+ _.newline = _.optSpace = _.indentUnit = _.indent = '';
+
+ _.space = _.optSpace ? _.optSpace : ' ';
+ parentheses = options.format.parentheses;
+ semicolons = options.format.semicolons;
+ safeConcatenation = options.format.safeConcatenation;
+ directive = options.directive;
+ parse = json ? null : options.parse;
+ extra = options;
+
+ if (extra.verbatim)
+ ExprGen = createExprGenWithExtras();
+
+ else
+ ExprGen = ExprRawGen;
+
+ return run($node);
+ };
\ No newline at end of file
diff --git a/src/Corrosion/lib/html.js b/src/Corrosion/lib/html.js
new file mode 100644
index 00000000..f610c67a
--- /dev/null
+++ b/src/Corrosion/lib/html.js
@@ -0,0 +1,226 @@
+const parse5 = require('parse5');
+
+class HTMLRewriter {
+ constructor(ctx) {
+ this.ctx = ctx;
+ this.attrs = [
+ {
+ tags: ['form', 'object', 'a', 'link', 'area', 'base', 'script', 'img', 'audio', 'video', 'input', 'embed', 'iframe', 'track', 'source', 'html', 'table', 'head'],
+ attrs: ['src', 'href', 'ping', 'data', 'movie', 'action', 'poster', 'profile', 'background'],
+ handler: 'url',
+ },
+ {
+ tags: ['iframe'],
+ attrs: ['srcdoc'],
+ handler: 'html',
+ },
+ {
+ tags: ['img', 'link', 'source'],
+ attrs: ['srcset', 'imagesrcset'],
+ handler: 'srcset',
+ },
+ {
+ tags: '*',
+ attrs: ['style'],
+ handler: 'css',
+ },
+ {
+ tags: '*',
+ attrs: ['http-equiv', 'integrity', 'nonce', 'crossorigin'],
+ handler: 'delete',
+ },
+ ];
+ };
+ process(source, config = {}) {
+ const ast = parse5[config.document ? 'parse' : 'parseFragment'](source);
+ const meta = {
+ origin: config.origin,
+ base: new URL(config.base),
+ };
+ iterate(ast, node => {
+ if (!node.tagName) return;
+ switch(node.tagName) {
+ case 'STYLE':
+ if (node.textContent) node.textContent = this.ctx.css.process(node.textContent, meta);
+ break;
+ case 'TITLE':
+ if (node.textContent && this.ctx.config.title) node.textContent = this.ctx.config.title;
+ break;
+ case 'SCRIPT':
+ if (node.getAttribute('type') != 'application/json' && node.textContent) node.textContent = this.ctx.js.process(node.textContent);
+ break;
+ case 'BASE':
+ if (node.hasAttribute('href')) meta.base = new URL(node.getAttribute('href'), config.base);
+ break;
+ };
+ node.attrs.forEach(attr => {
+ const handler = this.attributeRoute({
+ ...attr,
+ node,
+ });
+ let flags = [];
+ if (node.tagName == 'SCRIPT' && attr.name == 'src') flags.push('js');
+ if (node.tagName == 'LINK' && node.getAttribute('rel') == 'stylesheet') flags.push('css');
+ switch(handler) {
+ case 'url':
+ node.setAttribute(`corrosion-${attr.name}`, attr.value);
+ attr.value = this.ctx.url.wrap(attr.value, { ...meta, flags });
+ break;
+ case 'srcset':
+ node.setAttribute(`corrosion-${attr.name}`, attr.value);
+ attr.value = this.srcset(attr.value, meta);
+ break;
+ case 'css':
+ attr.value = this.ctx.css.process(attr.value, { ...meta, context: 'declarationList' });
+ break;
+ case 'html':
+ node.setAttribute(`corrosion-${attr.name}`, attr.value);
+ attr.value = this.process(attr.value, { ...config, ...meta });
+ break;
+ case 'delete':
+ node.removeAttribute(attr.name);
+ break;
+ };
+ });
+ });
+ if (config.document) {
+ for (let i in ast.childNodes) if (ast.childNodes[i].tagName == 'html') ast.childNodes[i].childNodes.forEach(node => {
+ if (node.tagName == 'head') {
+ node.childNodes.unshift(...this.injectHead(config.base));
+ };
+ });
+ };
+ return parse5.serialize(ast);
+ };
+ source(processed, config = {}) {
+ const ast = parse5[config.document ? 'parse' : 'parseFragment'](processed);
+ iterate(ast, node => {
+ if (!node.tagName) return;
+ node.attrs.forEach(attr => {
+ if (node.hasAttribute(`corrosion-${attr.name}`)) {
+ attr.value = node.getAttribute(`corrosion-${attr.name}`);
+ node.removeAttribute(`corrosion-${attr.name}`)
+ };
+ });
+ });
+ return parse5.serialize(ast);
+ };
+ injectHead() {
+ return [
+ {
+ nodeName: 'title',
+ tagName: 'title',
+ childNodes: [
+ {
+ nodeName: '#text',
+ value: this.ctx.config.title || '',
+ }
+ ],
+ attrs: [],
+ },
+ {
+ nodeName: 'script',
+ tagName: 'script',
+ childNodes: [],
+ attrs: [
+ {
+ name: 'src',
+ value: this.ctx.prefix + 'index.js',
+ },
+ ],
+ },
+ {
+ nodeName: 'script',
+ tagName: 'script',
+ childNodes: [
+ {
+ nodeName: '#text',
+ value: `window.$corrosion = new Corrosion({ window, codec: '${this.ctx.config.codec || 'plain'}', prefix: '${this.ctx.prefix}', ws: ${this.ctx.config.ws}, cookie: ${this.ctx.config.cookie}, title: ${typeof this.ctx.config.title == 'boolean' ? this.ctx.config.title : '\'' + this.ctx.config.title + '\''}, }); $corrosion.init(); document.currentScript.remove();`
+ },
+ ],
+ attrs: [],
+ }
+ ];
+ }
+ attributeRoute(data) {
+ const match = this.attrs.find(entry => entry.tags == '*' && entry.attrs.includes(data.name) || entry.tags.includes(data.node.tagName.toLowerCase()) && entry.attrs.includes(data.name));
+ return match ? match.handler : false;
+ };
+ srcset(val, config = {}) {
+ return val.split(',').map(src => {
+ const parts = src.trimStart().split(' ');
+ if (parts[0]) parts[0] = this.ctx.url.wrap(parts[0], config);
+ return parts.join(' ');
+ }).join(', ');
+ };
+ unsrcset(val, config = {}) {
+ return val.split(',').map(src => {
+ const parts = src.trimStart().split(' ');
+ if (parts[0]) parts[0] = this.ctx.url.unwrap(parts[0], config);
+ return parts.join(' ');
+ }).join(', ');
+ };
+};
+
+class Parse5Wrapper {
+ constructor(node){
+ this.raw = node || {
+ attrs: [],
+ childNodes: [],
+ namespaceURI: '',
+ nodeName: '',
+ parentNode: {},
+ tagName: '',
+ };
+ };
+ hasAttribute(name){
+ return this.raw.attrs.some(attr => attr.name == name);
+ };
+ removeAttribute(name){
+ if (!this.hasAttribute(name)) return;
+ this.raw.attrs.splice(this.raw.attrs.findIndex(attr => attr.name == name), 1);
+ };
+ setAttribute(name, val = ''){
+ if (!name) return;
+ this.removeAttribute(name);
+ this.raw.attrs.push({
+ name: name,
+ value: val,
+ });
+ };
+ getAttribute(name){
+ return (this.raw.attrs.find(attr => attr.name == name) || { value: null }).value;
+ };
+ get textContent(){
+ return (this.raw.childNodes.find(node => node.nodeName == '#text') || { value: '', }).value
+ };
+ set textContent(val){
+ if (this.raw.childNodes.some(node => node.nodeName == '#text')) return this.raw.childNodes[this.raw.childNodes.findIndex(node => node.nodeName == '#text')].value = val;
+ this.raw.childNodes.push({
+ nodeName: '#text',
+ value: val,
+ });
+ };
+ get tagName(){
+ return (this.raw.tagName || '').toUpperCase();
+ };
+ get nodeName(){
+ return this.raw.nodeName;
+ };
+ get parentNode(){
+ return this.raw.parentNode;
+ };
+ get childNodes(){
+ return this.raw.childNodes || [];
+ };
+ get attrs() {
+ return this.raw.attrs || [];
+ };
+};
+
+function iterate(ast, fn = (node = Parse5Wrapper.prototype) => null) {
+ fn(new Parse5Wrapper(ast));
+ if (ast.childNodes) for (let i in ast.childNodes) iterate(ast.childNodes[i], fn);
+};
+
+module.exports = HTMLRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/js.js b/src/Corrosion/lib/js.js
new file mode 100644
index 00000000..4e6992ba
--- /dev/null
+++ b/src/Corrosion/lib/js.js
@@ -0,0 +1,185 @@
+const { parse } = require('acorn-hammerhead');
+const { generate } = require('./esotope');
+
+class JSRewriter {
+ constructor(ctx) {
+ this.parseOptions = {
+ allowReturnOutsideFunction: true,
+ allowImportExportEverywhere: true,
+ ecmaVersion: 2021,
+ };
+ this.generationOptions = {
+ format: {
+ quotes: 'double',
+ escapeless: true,
+ compact: true,
+ },
+ };
+ this.rewrite = ['location', 'parent', 'top'];
+ this.map = [
+ {
+ type: 'MemberExpression',
+ handler: (node, parent) => {
+ let rewrite = false;
+ if (parent.type == 'UnaryExpression' && parent.operator == 'delete') return;
+ if (parent.type == 'NewExpression' && parent.callee == node) return;
+ if (parent.type === 'CallExpression' && parent.callee === node) return;
+ if (node.preventRewrite) return;
+ switch(node.property.type) {
+ case 'Identifier':
+ //if (node.computed) rewrite = true;
+ if (!node.computed && this.rewrite.includes(node.property.name)) {
+ node.property = this.createLiteral(node.property.name);
+ rewrite = true;
+ };
+ break;
+ case 'Literal':
+ if (this.rewrite.includes(node.property.name)) rewrite = true;
+ break;
+ case 'TemplateLiteral':
+ rewrite = true;
+ break;
+ default:
+ if (node.computed) rewrite = true;
+ };
+ if (rewrite) {
+ let identifier = '$corrosionGet$m';
+ let nodeToRewrite = node;
+ const args = [
+ node.object,
+ node.property,
+ ];
+ if (node.computed) args[1].preventRewrite = true;
+ if (parent.type == 'AssignmentExpression' && parent.left == node) {
+ identifier = '$corrosionSet$m';
+ nodeToRewrite = parent;
+ args.push(parent.right, this.createLiteral(parent.operator));
+ };
+ if (parent.type == 'CallExpression' && parent.callee == node) {
+ identifier = '$corrosionCall$m';
+ nodeToRewrite = parent;
+ args.push(this.createArrayExpression(...parent.arguments))
+ };
+ if (parent.type == 'UpdateExpression') {
+ identifier = '$corrosionSet$m';
+ nodeToRewrite = parent;
+ args.push(this.createLiteral(null), this.createLiteral(parent.operator));
+ };
+ Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier, }, args));
+ };
+ },
+ },
+ {
+ type: 'Identifier',
+ handler: (node, parent) => {
+ if (parent.type == 'MemberExpression' && parent.property == node) return; // window.location;
+ if (parent.type == 'LabeledStatement') return; // { location: null, };
+ if (parent.type == 'VariableDeclarator' && parent.id == node) return;
+ if (parent.type == 'Property' && parent.key == node) return;
+ if (parent.type == 'MethodDefinition') return;
+ if (parent.type == 'ClassDeclaration') return;
+ if (parent.type == 'RestElement') return;
+ if (parent.type == 'ExportSpecifier') return;
+ if (parent.type == 'ImportSpecifier') return;
+ if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression' || parent.type == 'ArrowFunctionExpression') && parent.params.includes(node)) return;
+ if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression') && parent.id == node) return;
+ if (parent.type == 'AssignmentPattern' && parent.left == node) return;
+ if (!this.rewrite.includes(node.name)) return;
+ if (node.preventRewrite) return;
+ let identifier = '$corrosionGet$';
+ let nodeToRewrite = node;
+ const args = [
+ this.createIdentifier(node.name, true),
+ ];
+
+ if (parent.type == 'AssignmentExpression' && parent.left == node) {
+ identifier = '$corrosionSet$';
+ nodeToRewrite = parent;
+ args.push(parent.right);
+ args.push(this.createLiteral(parent.operator));
+ };
+
+ Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier }, args));
+ },
+ },
+ {
+ type: 'ImportDeclaration',
+ handler: (node, parent, url) => {
+ if (node.source.type != 'Literal' || !url) return;
+ node.source = this.createLiteral(ctx.url.wrap(node.source.value, { base: url, }));
+ },
+ },
+ {
+ type: 'ImportExpression',
+ handler: (node, parent) => {
+ node.source = this.createCallExpression(this.createMemberExpression(this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('url')), this.createIdentifier('wrap')), [
+ node.source,
+ this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('meta')),
+ ]);
+ },
+ },
+ ];
+ this.ctx = ctx;
+ };
+ process(source, url) {
+ try {
+ const ast = parse(source, this.parseOptions);
+ this.iterate(ast, (node, parent) => {
+ const fn = this.map.find(entry => entry.type == (node || {}).type);
+ if (fn) fn.handler(node, parent, url);
+ });
+ return (url ? this.createHead(url) : '') + generate(ast, this.generationOptions);
+ } catch(e) {
+ return source;
+ };
+ };
+ createHead(url) {
+ return `
+ if (!self.$corrosion && self.importScripts) {
+ importScripts(location.origin + '${this.ctx.prefix}index.js');
+ self.$corrosion = new Corrosion({ url: '${url}', codec: '${this.ctx.config.codec || 'plain'}', serviceWorker: true, window: self, prefix: '${this.ctx.prefix || '/service/'}', ws: ${this.ctx.config.ws || true}, cookies: ${this.ctx.config.cookies || false}, title: '${this.ctx.config.title}', }); $corrosion.init();
+ };\n`;
+ };
+ iterate(ast, handler) {
+ if (typeof ast != 'object' || !handler) return;
+ walk(ast, null, handler);
+ function walk(node, parent, handler) {
+ if (typeof node != 'object' || !handler) return;
+ handler(node, parent, handler);
+ for (const child in node) {
+ if (Array.isArray(node[child])) {
+ node[child].forEach(entry => walk(entry, node, handler));
+ } else {
+ walk(node[child], node, handler);
+ };
+ };
+ };
+ };
+ createCallExpression(callee, args) {
+ return { type: 'CallExpression', callee, arguments: args, optional: false, };
+ };
+ createArrayExpression(...elements) {
+ return {
+ type: 'ArrayExpression',
+ elements,
+ };
+ };
+ createMemberExpression(object, property) {
+ return {
+ type: 'MemberExpression',
+ object,
+ property,
+ };
+ };
+ createLiteral(value) {
+ return {
+ type: 'Literal',
+ value,
+ }
+ };
+ createIdentifier(name, preventRewrite) {
+ return { type: 'Identifier', name, preventRewrite: preventRewrite || false, };
+ };
+};
+
+module.exports = JSRewriter;
\ No newline at end of file
diff --git a/src/Corrosion/lib/rewrite.js b/src/Corrosion/lib/rewrite.js
new file mode 100644
index 00000000..0a1fc05d
--- /dev/null
+++ b/src/Corrosion/lib/rewrite.js
@@ -0,0 +1,27 @@
+const URLWrapper = require('./url');
+const CookieRewriter = require('./cookie');
+const CSSRewriter = require('./css');
+const HTMLRewriter = require('./html');
+const JSRewriter = require('./js');
+const defaultConfig = {
+ prefix: '/service/',
+ codec: 'plain',
+ ws: true,
+ cookie: true,
+ title: 'Service',
+};
+
+class Rewrite {
+ constructor(config = defaultConfig) {
+ this.config = Object.assign(defaultConfig, config);
+ this.prefix = this.config.prefix;
+ this.url = new URLWrapper(this.config || {});
+ this.codec = this.url.codec;
+ this.cookies = new CookieRewriter(this);
+ this.css = new CSSRewriter(this);
+ this.js = new JSRewriter(this);
+ this.html = new HTMLRewriter(this);
+ };
+};
+
+module.exports = Rewrite;
\ No newline at end of file
diff --git a/src/Corrosion/lib/server/bundle.js b/src/Corrosion/lib/server/bundle.js
new file mode 100644
index 00000000..fa3700b2
--- /dev/null
+++ b/src/Corrosion/lib/server/bundle.js
@@ -0,0 +1,30109 @@
+/******/ (() => { // webpackBootstrap
+/******/ var __webpack_modules__ = ([
+/* 0 */,
+/* 1 */
+/***/ ((module) => {
+
+function createDocumentRewriter(ctx) {
+ return function rewriteDocument() {
+ if (ctx.serviceWorker) return;
+ const {
+ HTMLMediaElement,
+ HTMLScriptElement,
+ HTMLAudioElement,
+ HTMLVideoElement,
+ HTMLInputElement,
+ HTMLEmbedElement,
+ HTMLTrackElement,
+ HTMLAnchorElement,
+ HTMLIFrameElement,
+ HTMLAreaElement,
+ HTMLLinkElement,
+ HTMLBaseElement,
+ HTMLFormElement,
+ HTMLImageElement,
+ HTMLSourceElement,
+ } = ctx.window;
+ const cookie = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'cookie');
+ const domain = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'domain');
+ const title = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'title');
+ const baseURI = Object.getOwnPropertyDescriptor(ctx.window.Node.prototype, 'baseURI');
+ const cookieEnabled = Object.getOwnPropertyDescriptor(ctx.window.Navigator.prototype, 'cookieEnabled');
+ let spoofTitle = '';
+ let spoofDomain = ctx.location.hostname;
+
+ if (ctx.window.Document.prototype.write) {
+ ctx.window.Document.prototype.write = new Proxy(ctx.window.Document.prototype.write, {
+ apply: (target, that , args) => {
+ if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Document.prototype.hasOwnProperty('cookie')) {
+ Object.defineProperty(ctx.window.Document.prototype, 'cookie', {
+ get: new Proxy(cookie.get, {
+ apply: (target, that, args) => {
+ const cookies = Reflect.apply(target, that, args);
+ return ctx.config.cookie ? ctx.cookies.decode(cookies, ctx.meta) : '';
+ },
+ }),
+ set: new Proxy(cookie.set, {
+ apply: (target, that, [ val ]) => {
+ return Reflect.apply(target, that, [ ctx.config.cookie ? ctx.cookies.encode(val, ctx.meta) : '' ]);
+ },
+ }),
+ });
+ };
+ if (ctx.window.Document.prototype.writeln) {
+ ctx.window.Document.prototype.writeln = new Proxy(ctx.window.Document.prototype.writeln, {
+ apply: (target, that , args) => {
+ if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Element.prototype.setAttribute) {
+ ctx.window.Element.prototype.setAttribute = new Proxy(ctx.window.Element.prototype.setAttribute, {
+ apply: (target, that, args) => {
+ if (args[0] && args[1]) {
+ const handler = ctx.html.attributeRoute({
+ name: args[0],
+ value: args[1],
+ node: that,
+ });
+ switch(handler) {
+ case 'url':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ //if (that.tagName == 'SCRIPT' && args[0] == 'src') flags.push('js');
+ args[1] = ctx.url.wrap(args[1], ctx.meta);
+ break;
+ case 'srcset':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.html.srcset(args[1], ctx.meta);
+ break;
+ case 'css':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.css.process(args[1], { ...ctx.meta, context: 'declarationList' });
+ break;
+ case 'html':
+ Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ args[1] = ctx.html.process(args[1], ctx.meta);
+ break;
+ case 'delete':
+ return Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
+ };
+ };
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Element.prototype.getAttribute) {
+ ctx.window.Element.prototype.getAttribute = new Proxy(ctx.window.Element.prototype.getAttribute, {
+ apply: (target, that, args) => {
+ if (args[0] && that.hasAttribute(`corrosion-${args[0]}`)) args[0] = `corrosion-${args[0]}`;
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ ctx.window.CSSStyleDeclaration.prototype.setProperty = new Proxy(ctx.window.CSSStyleDeclaration.prototype.setProperty, {
+ apply: (target, that, args) => {
+ if (args[1]) args[1] = ctx.css.process(args[1], { context: 'value', ...ctx.meta, });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ if (ctx.window.Audio) {
+ ctx.window.Audio = new Proxy(ctx.window.Audio, {
+ construct: (target, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ [
+ 'innerHTML',
+ 'outerHTML',
+ ].forEach(html => {
+ const descriptor = Object.getOwnPropertyDescriptor(ctx.window.Element.prototype, html);
+ Object.defineProperty(ctx.window.Element.prototype, html, {
+ get: new Proxy(descriptor.get, {
+ apply: (target, that, args) => {
+ const body = Reflect.apply(target, that, args);
+ if (!body || html == 'innerHTML' && that.tagName == 'SCRIPT') return body;
+ return ctx.html.source(body, ctx.meta);
+ },
+ }),
+ set: new Proxy(descriptor.set, {
+ apply(target, that, [ val ]) {
+ return Reflect.apply(target, that, [ val ? ctx.html.process(val.toString(), ctx.meta) : val, ]);
+ },
+ }),
+ });
+ });
+ [
+ ['background', 'background'],
+ ['backgroundImage', 'background-image'],
+ ['listStyleImage', 'list-style-image'],
+ ].forEach(([key, cssProperty]) => {
+ Object.defineProperty(ctx.window.CSS2Properties ? ctx.window.CSS2Properties.prototype : ctx.window.CSSStyleDeclaration.prototype, key, {
+ get() {
+ return this.getPropertyValue(cssProperty);
+ },
+ set(val) {
+ return this.setProperty(cssProperty, val);
+ },
+ });
+ });
+ Object.defineProperty(ctx.window.Document.prototype, 'domain', {
+ get: new Proxy(domain.get, {
+ apply: () => spoofDomain,
+ }),
+ set: new Proxy(domain.set, {
+ apply: (target, that, [ val ]) => {
+ if (!val.toString().endsWith(ctx.location.hostname.split('.').slice(-2).join('.'))) return Reflect.apply(target, that, ['']);
+ return spoofDomain = val;
+ },
+ }),
+ });
+ if (ctx.config.title) Object.defineProperty(ctx.window.Document.prototype, 'title', {
+ get: new Proxy(title.get, {
+ apply: () => spoofTitle,
+ }),
+ set: new Proxy(title.set, {
+ apply: (target, that, [ val ]) => spoofTitle = val,
+ }),
+ });
+ Object.defineProperty(ctx.window.Navigator.prototype, 'cookieEnabled', {
+ get: new Proxy(cookieEnabled.get, {
+ apply: () => ctx.config.cookie,
+ }),
+ });
+ Object.defineProperty(ctx.window.Node.prototype, 'baseURI', {
+ get: new Proxy(baseURI.get, {
+ apply: (target, that, args) => {
+ const val = Reflect.apply(target, that, args);
+ return val.startsWith(ctx.meta.origin) ? ctx.url.unwrap(val, ctx.meta) : val;
+ },
+ }),
+ });
+ [
+ {
+ elements: [ HTMLScriptElement, HTMLMediaElement, HTMLImageElement, HTMLAudioElement, HTMLVideoElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement],
+ properties: ['src'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLFormElement ],
+ properties: ['action'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement ],
+ properties: ['href'],
+ handler: 'url',
+ },
+ {
+ elements: [ HTMLImageElement, HTMLSourceElement ],
+ properties: ['srcset'],
+ handler: 'srcset',
+ },
+ {
+ elements: [ HTMLScriptElement ],
+ properties: ['integrity'],
+ handler: 'delete',
+ },
+ {
+ elements: [ HTMLIFrameElement ],
+ properties: ['contentWindow'],
+ handler: 'window',
+ },
+ ].forEach(entry => {
+ entry.elements.forEach(element => {
+ if (!element) return;
+ entry.properties.forEach(property => {
+ if (!element.prototype.hasOwnProperty(property)) return;
+ const descriptor = Object.getOwnPropertyDescriptor(element.prototype, property);
+ Object.defineProperty(element.prototype, property, {
+ get: descriptor.get ? new Proxy(descriptor.get, {
+ apply: (target, that, args) => {
+ let val = Reflect.apply(target, that, args);
+ let flags = [];
+ switch(entry.handler) {
+ case 'url':
+ //if (that.tagName == 'SCRIPT' && property == 'src') flags.push('js');
+ val = ctx.url.unwrap(val, ctx.meta);
+ break;
+ case 'srcset':
+ val = ctx.html.unsrcset(val, ctx.meta);
+ break;
+ case 'delete':
+ val = that.getAttribute(`corrosion-${property}`);
+ break;
+ case 'window':
+ try {
+ if (!val.$corrosion) {
+ val.$corrosion = new ctx.constructor({ ...ctx.config, window: val, });
+ val.$corrosion.init();
+ val.$corrosion.meta = ctx.meta;
+ };
+ } catch(e) {};
+ };
+ return val;
+ },
+ }) : undefined,
+ set: descriptor.set ? new Proxy(descriptor.set, {
+ apply(target, that, [ val ]) {
+ let newVal = val;
+ switch(entry.handler) {
+ case 'url':
+ newVal = ctx.url.wrap(newVal, ctx.meta);
+ break;
+ case 'srcset':
+ newVal = ctx.html.srcset(newVal, ctx.meta);
+ break;
+ case 'delete':
+ that.setAttribute(property, newVal);
+ return newVal;
+ };
+ return Reflect.apply(target, that, [ newVal ]);
+ },
+ }) : undefined,
+ });
+ });
+ });
+ });
+ };
+};
+module.exports = createDocumentRewriter;
+
+/***/ }),
+/* 2 */
+/***/ ((module) => {
+
+function createHistoryRewriter(ctx) {
+ return function rewriteHistory() {
+ if (ctx.serviceWorker) return;
+ if (ctx.window.History.prototype.pushState) {
+ ctx.window.History.prototype.pushState = new Proxy(ctx.window.History.prototype.pushState, {
+ apply: (target, that, args) => {
+ if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
+ const ret = Reflect.apply(target, that, args);
+ ctx.updateLocation();
+ return ret;
+ },
+ });
+ };
+ if (ctx.window.History.prototype.replaceState) {
+ ctx.window.History.prototype.replaceState = new Proxy(ctx.window.History.prototype.replaceState, {
+ apply: (target, that, args) => {
+ if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
+ const ret = Reflect.apply(target, that, args);
+ ctx.updateLocation();
+ return ret;
+ },
+ });
+ };
+ };
+};
+module.exports = createHistoryRewriter;
+
+/***/ }),
+/* 3 */
+/***/ ((module) => {
+
+function createHttpRewriter(ctx = {}) {
+ return function rewriteHttp() {
+ if (ctx.window.Request) {
+ const requestURL = Object.getOwnPropertyDescriptor(ctx.window.Request.prototype, 'url');
+ ctx.window.Request = new Proxy(ctx.window.Request, {
+ construct(target, args) {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], })
+ return Reflect.construct(target, args);
+ },
+ });
+ Object.defineProperty(ctx.window.Request.prototype, 'url', {
+ get: new Proxy(requestURL.get, {
+ apply: (target, that, args) => {
+ var url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.Response) {
+ const responseURL = Object.getOwnPropertyDescriptor(ctx.window.Response.prototype, 'url');
+ Object.defineProperty(ctx.window.Response.prototype, 'url', {
+ get: new Proxy(responseURL.get, {
+ apply: (target, that, args) => {
+ var url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.open) {
+ ctx.window.open = new Proxy(ctx.window.open, {
+ apply: (target, that, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target, that, args)
+ },
+ });
+ };
+ if (ctx.window.fetch) {
+ ctx.window.fetch = new Proxy(ctx.window.fetch, {
+ apply: (target, that, args) => {
+ if (args[0] instanceof ctx.window.Request) return Reflect.apply(target, that, args);
+ if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.Navigator && ctx.window.Navigator.prototype.sendBeacon) {
+ ctx.window.Navigator.prototype.sendBeacon = new Proxy(ctx.window.Navigator.prototype.sendBeacon, {
+ apply: (target, that, args) => {
+ if (args[0]) ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.XMLHttpRequest) {
+ const responseURL = Object.getOwnPropertyDescriptor(ctx.window.XMLHttpRequest.prototype, 'responseURL');
+ ctx.window.XMLHttpRequest.prototype.open = new Proxy(ctx.window.XMLHttpRequest.prototype.open, {
+ apply: (target, that, args) => {
+ if (args[1]) args[1] = ctx.url.wrap(args[1], { ...ctx.meta, flags: ['xhr'], });
+ return Reflect.apply(target, that, args);
+ },
+ });
+ Object.defineProperty(ctx.window.XMLHttpRequest.prototype, 'responseURL', {
+ get: new Proxy(responseURL.get, {
+ apply: (target, that, args) => {
+ const url = Reflect.apply(target, that, args);
+ return url ? ctx.url.unwrap(url, ctx.meta) : url;
+ },
+ }),
+ });
+ };
+ if (ctx.window.postMessage) {
+ ctx.window.postMessage = new Proxy(ctx.window.postMessage, {
+ apply: (target, that, args) => {
+ if (!ctx.serviceWorker && args[1]) args[1] = ctx.meta.origin;
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ if (ctx.window.WebSocket && ctx.config.ws) {
+ ctx.window.WebSocket = new Proxy(ctx.window.WebSocket, {
+ construct: (target, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0].toString().replace('ws', 'http'), ctx.meta).replace('http', 'ws') + '?origin=' + ctx.location.origin;
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ };
+};
+
+module.exports = createHttpRewriter;
+
+/***/ }),
+/* 4 */
+/***/ ((module) => {
+
+class Location {
+ get [Symbol.toPrimitive]() {
+ return () => this.href;
+ };
+};
+
+function createLocation(ctx, url) {
+ const _location = new Location();
+ const _url = new URL(url);
+ [
+ 'hash',
+ 'host',
+ 'hostname',
+ 'href',
+ 'pathname',
+ 'port',
+ 'protocol',
+ 'search',
+ 'origin',
+ ].forEach(property => {
+ Object.defineProperty(_location, property, {
+ get() {
+ return _url[property];
+ },
+ set(val) {
+ if (ctx.serviceWorker || property == 'origin') return;
+ if (property == 'href') {
+ return ctx.window.location.href = ctx.url.wrap(new URL(val, _url).href);
+ };
+ _url[property] = val;
+ return ctx.window.location.href = ctx.url.wrap(_url);
+ },
+ });
+ });
+ if (!ctx.serviceWorker) [
+ 'assign',
+ 'replace',
+ 'reload',
+ ].forEach(method => {
+ _location[method] = new Proxy(ctx.window.location[method], {
+ apply(target, that, args) {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target.bind(ctx.window.location), that, args);
+ },
+ });
+ });
+ _location.toString = new Proxy(_url.toString, {
+ apply(target, that, args) {
+ return Reflect.apply(target.bind(_url), that, args);
+ },
+ });
+ return _location;
+};
+
+createLocation.Location = Location;
+module.exports = createLocation;
+
+/***/ }),
+/* 5 */
+/***/ ((module) => {
+
+function createWorkerRewriter(ctx = {}) {
+ return function rewriteWorker() {
+ if (ctx.window.Worker) {
+ ctx.window.Worker = new Proxy(ctx.window.Worker, {
+ construct: (target, args) => {
+ if (args[0]) {
+ if (args[0].trim().startsWith(`blob:${ctx.window.location.origin}`)) {
+ const xhr = new ctx.originalXhr();
+ xhr.open('GET', args[0], false);
+ xhr.send();
+ const script = ctx.js.process(xhr.responseText, ctx.location.origin + args[0].trim().slice(`blob:${ctx.window.location.origin}`.length));
+ const blob = new Blob([ script ], { type: 'application/javascript' });
+ args[0] = URL.createObjectURL(blob);
+ } else {
+ args[0] = ctx.url.wrap(args[0], ctx.meta);
+ };
+ };
+ return Reflect.construct(target, args);
+ },
+ });
+ };
+ if (ctx.serviceWorker && ctx.window.importScripts) {
+ ctx.window.importScripts = new Proxy(ctx.window.importScripts, {
+ apply: (target, that, args) => {
+ if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
+ return Reflect.apply(target, that, args);
+ },
+ });
+ };
+ };
+};
+
+module.exports = createWorkerRewriter;
+
+/***/ }),
+/* 6 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+const URLWrapper = __webpack_require__(7);
+const CookieRewriter = __webpack_require__(9);
+const CSSRewriter = __webpack_require__(11);
+const HTMLRewriter = __webpack_require__(134);
+const JSRewriter = __webpack_require__(159);
+const defaultConfig = {
+ prefix: '/service/',
+ codec: 'plain',
+ ws: true,
+ cookie: true,
+ title: 'Service',
+};
+
+class Rewrite {
+ constructor(config = defaultConfig) {
+ this.config = Object.assign(defaultConfig, config);
+ this.prefix = this.config.prefix;
+ this.url = new URLWrapper(this.config || {});
+ this.codec = this.url.codec;
+ this.cookies = new CookieRewriter(this);
+ this.css = new CSSRewriter(this);
+ this.js = new JSRewriter(this);
+ this.html = new HTMLRewriter(this);
+ };
+};
+
+module.exports = Rewrite;
+
+/***/ }),
+/* 7 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+const codec = __webpack_require__(8);
+const defaultConfig = {
+ prefix: '/service/',
+ codec: 'plain'
+};
+
+class URLWrapper {
+ constructor(config = defaultConfig) {
+ this.prefix = config.prefix || defaultConfig.prefix;
+ this.codec = codec[config.codec || 'plain'] || codec['plain'];
+ this.regex = /^(#|about:|data:|blob:|mailto:|javascript:)/;
+ };
+ wrap(val, config = {}) {
+ if (!val || this.regex.test(val)) return val;
+ let flags = '';
+ (config.flags || []).forEach(flag => flags += `${flag}_/`);
+ if (config.base) try {
+ if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(val, config.base).protocol)) return val;
+ } catch(e) {
+ return val;
+ };
+ return (config.origin || '') + this.prefix + flags + this.codec.encode(config.base ? new URL(val, config.base) : val) + '/';
+ };
+ unwrap(val, config = {}) {
+ if (!val || this.regex.test(val)) return val;
+ let processed = val.slice((config.origin || '').length + this.prefix.length);
+ const flags = ('/' + processed).match(/(?<=\/)(.*?)(?=_\/)/g) || [];
+ flags.forEach(flag => processed = processed.slice(`${flag}_/`.length));
+ let [ url, leftovers ] = processed.split(/\/(.+)?/);
+ return config.flags ? { value: this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : ''), flags } : this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : '');
+ };
+};
+
+module.exports = URLWrapper;
+
+/***/ }),
+/* 8 */
+/***/ ((__unused_webpack_module, exports) => {
+
+exports.xor = {
+ encode(str){
+ if (!str) return str;
+ return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join(''));
+ },
+ decode(str){
+ if (!str) return str;
+ return decodeURIComponent(str).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join('');
+ },
+};
+exports.plain = {
+ encode(str) {
+ if (!str) return str;
+ return encodeURIComponent(str);
+ },
+ decode(str) {
+ if (!str) return str;
+ return decodeURIComponent(str);
+ },
+};
+exports.base64 = {
+ encode(str){
+ if (!str) return str;
+ str = str.toString();
+ const b64chs = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
+ let u32;
+ let c0;
+ let c1;
+ let c2;
+ let asc = '';
+ let pad = str.length % 3;
+
+ for (let i = 0; i < str.length;) {
+ if((c0 = str.charCodeAt(i++)) > 255 || (c1 = str.charCodeAt(i++)) > 255 || (c2 = str.charCodeAt(i++)) > 255)throw new TypeError('invalid character found');
+ u32 = (c0 << 16) | (c1 << 8) | c2;
+ asc += b64chs[u32 >> 18 & 63]
+ + b64chs[u32 >> 12 & 63]
+ + b64chs[u32 >> 6 & 63]
+ + b64chs[u32 & 63];
+ }
+
+ return encodeURIComponent(pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc);
+ },
+ decode(str){
+ if (!str) return str;
+ str = decodeURIComponent(str.toString());
+ const b64tab = {"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"+":62,"/":63,"=":64};
+ str = str.replace(/\s+/g, '');
+ str += '=='.slice(2 - (str.length & 3));
+ let u24;
+ let bin = '';
+ let r1;
+ let r2;
+
+ for (let i = 0; i < str.length;) {
+ u24 = b64tab[str.charAt(i++)] << 18
+ | b64tab[str.charAt(i++)] << 12
+ | (r1 = b64tab[str.charAt(i++)]) << 6
+ | (r2 = b64tab[str.charAt(i++)]);
+ bin += r1 === 64 ? String.fromCharCode(u24 >> 16 & 255)
+ : r2 === 64 ? String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255)
+ : String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
+ };
+ return bin;
+ },
+};
+
+/***/ }),
+/* 9 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+const { SetCookie, CookieStore } = __webpack_require__(10);
+
+class CookieRewriter {
+ constructor(ctx) {
+ this.ctx = ctx;
+ };
+ decode(store, config = {}) {
+ const url = new URL(config.url);
+ const cookies = new CookieStore(store);
+ cookies.forEach((val, key) => {
+ if (!key.includes('@') || key.slice(key.length - url.hostname.length) != url.hostname) return cookies.delete(key);
+ cookies.delete(key);
+ cookies.set(key.substr(0, key.length - url.hostname.length - 1), val);
+ });
+ return cookies.serialize();
+ };
+ encode(input, config = {}) {
+ if (Array.isArray(input)) {
+ const rw = [ ...input ];
+ for (let i in rw) rw[i] = this.encode(rw[i], config);
+ return rw;
+ };
+ const url = new URL(config.url);
+ const cookie = new SetCookie(input);
+ if (!cookie.name) return null;
+ cookie.domain = config.domain;
+ cookie.secure = config.secure;
+ cookie.name += `@${url.hostname}`;
+ cookie.path = this.ctx.prefix;
+ return cookie.serialize() || '';
+ };
+};
+
+module.exports = CookieRewriter;
+
+/***/ }),
+/* 10 */
+/***/ ((__unused_webpack_module, exports) => {
+
+// -------------------
+// This file is shared both by the server and client.
+// Do not include any browser or node specific APIs
+// -------------------
+
+class CookieStore {
+ constructor(val = ''){
+ this.data = {};
+ val.split(';').map(cookie => {
+ var [ name, val = ''] = cookie.trimStart().split('=');
+ if (name) this.data[name] = val;
+ });
+ };
+ has(name){
+ if (!name || !this.data[name]) return false;
+ return true;
+ };
+ get(name){
+ return this.has(name) ? this.data[name] : null;
+ };
+ set(name, val){
+ if (!name || !val) return;
+ return this.data[name] = val;
+ };
+ delete(name){
+ if (!name) return;
+ return delete this.data[name];
+ };
+ forEach(action = (node, key) => null){
+ for (let prop in this.data) action(this.data[prop], prop);
+ };
+ serialize(){
+ var str = '';
+ for (let i in this.data) str += ` ${i}=${this.data[i]};`;
+ return str.substr(1);
+ };
+};
+
+class SetCookie {
+ constructor(val = ''){
+
+ var [ [ name, value = '' ], ...data ] = val.split(';').map(str => str.trimStart().split('='));
+
+ this.name = name;
+ this.value = value;
+ this.expires = null;
+ this.maxAge = null;
+ this.domain = null;
+ this.secure = false;
+ this.httpOnly = false;
+ this.path = null;
+ this.sameSite = null;
+
+ data.forEach(([name = null, value = null]) => {
+ if (typeof name == 'string') switch(name.toLowerCase()){
+ case 'domain':
+ this.domain = value;
+ break;
+ case 'secure':
+ this.secure = true;
+ break;
+ case 'httponly':
+ this.httpOnly = true;
+ break;
+ case 'samesite':
+ this.sameSite = value;
+ break;
+ case 'path':
+ this.path = value;
+ break;
+ case 'expires':
+ this.expires = value;
+ break;
+ case 'maxage':
+ this.maxAge = value;
+ break;
+ };
+ });
+ };
+ serialize(){
+ if (!this.name) return;
+ var str = `${this.name}=${this.value};`;
+ if (this.expires) str += ` Expires=${this.expires};`;
+ if (this.maxAge) str += ` Max-Age=${this.max_age};`;
+ if (this.domain) str += ` Domain=${this.domain};`;
+ if (this.secure) str += ` Secure;`;
+ if (this.httpOnly) str += ` HttpOnly;`;
+ if (this.path) str += ` Path=${this.path};`;
+ if (this.sameSite) str += ` SameSite=${this.sameSite};`;
+ return str;
+ };
+};
+
+exports.CookieStore = CookieStore;
+exports.SetCookie = SetCookie;
+
+/***/ }),
+/* 11 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+const csstree = __webpack_require__(12);
+
+class CSSRewriter {
+ constructor(ctx) {
+ this.ctx = ctx;
+ };
+ process(source, config = {}) {
+ const ast = csstree.parse(source, {
+ context: config.context || 'stylesheet',
+ parseCustomProperty: true,
+ });
+ const urls = csstree.findAll(ast, node =>
+ node.type == 'Url'
+ );
+ const imports = csstree.findAll(ast, node =>
+ node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
+ );
+ urls.forEach(({ value }) => {
+ switch(value.type) {
+ case 'String':
+ const quote = value.value.substring(0, 1);
+ value.value = quote + this.ctx.url.wrap(value.value.slice(1).slice(0, -1), config) + quote;
+ break;
+ case 'Raw':
+ value.value = this.ctx.url.wrap(value.value, config);
+ break;
+ };
+ });
+ imports.forEach(({ prelude }) => {
+ const { data } = prelude.children.head;
+ const quote = data.value.substring(0, 1);
+ data.value = quote + this.ctx.url.wrap(data.value.slice(1).slice(0, -1), config) + quote;
+ });
+ return csstree.generate(ast);
+ };
+ source(processed, config = {}) {
+ const ast = csstree.parse(processed, {
+ context: config.context || 'stylesheet',
+ parseCustomProperty: true,
+ });
+ const urls = csstree.findAll(ast, node =>
+ node.type == 'Url'
+ );
+ const imports = csstree.findAll(ast, node =>
+ node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
+ );
+ urls.forEach(({ value }) => {
+ switch(value.type) {
+ case 'String':
+ const quote = value.value.substring(0, 1);
+ value.value = quote + this.ctx.url.unwrap(value.value.slice(1).slice(0, -1), config) + quote;
+ break;
+ case 'Raw':
+ value.value = this.ctx.url.unwrap(value.value, config);
+ break;
+ };
+ });
+ imports.forEach(({ prelude }) => {
+ const { data } = prelude.children.head;
+ const quote = data.value.substring(0, 1);
+ data.value = quote + this.ctx.url.unwrap(data.value.slice(1).slice(0, -1), config) + quote;
+ });
+ return csstree.generate(ast);
+ };
+};
+
+module.exports = CSSRewriter;
+
+/***/ }),
+/* 12 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+module.exports = __webpack_require__(13);
+
+
+/***/ }),
+/* 13 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+function merge() {
+ var dest = {};
+
+ for (var i = 0; i < arguments.length; i++) {
+ var src = arguments[i];
+ for (var key in src) {
+ dest[key] = src[key];
+ }
+ }
+
+ return dest;
+}
+
+module.exports = __webpack_require__(14).create(
+ merge(
+ __webpack_require__(57),
+ __webpack_require__(104),
+ __webpack_require__(132)
+ )
+);
+module.exports.version = __webpack_require__(133).version;
+
+
+/***/ }),
+/* 14 */
+/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
+
+var List = __webpack_require__(15);
+var SyntaxError = __webpack_require__(16);
+var TokenStream = __webpack_require__(18);
+var Lexer = __webpack_require__(22);
+var definitionSyntax = __webpack_require__(41);
+var tokenize = __webpack_require__(27);
+var createParser = __webpack_require__(42);
+var createGenerator = __webpack_require__(45);
+var createConvertor = __webpack_require__(53);
+var createWalker = __webpack_require__(54);
+var clone = __webpack_require__(55);
+var names = __webpack_require__(25);
+var mix = __webpack_require__(56);
+
+function createSyntax(config) {
+ var parse = createParser(config);
+ var walk = createWalker(config);
+ var generate = createGenerator(config);
+ var convert = createConvertor(walk);
+
+ var syntax = {
+ List: List,
+ SyntaxError: SyntaxError,
+ TokenStream: TokenStream,
+ Lexer: Lexer,
+
+ vendorPrefix: names.vendorPrefix,
+ keyword: names.keyword,
+ property: names.property,
+ isCustomProperty: names.isCustomProperty,
+
+ definitionSyntax: definitionSyntax,
+ lexer: null,
+ createLexer: function(config) {
+ return new Lexer(config, syntax, syntax.lexer.structure);
+ },
+
+ tokenize: tokenize,
+ parse: parse,
+ walk: walk,
+ generate: generate,
+
+ find: walk.find,
+ findLast: walk.findLast,
+ findAll: walk.findAll,
+
+ clone: clone,
+ fromPlainObject: convert.fromPlainObject,
+ toPlainObject: convert.toPlainObject,
+
+ createSyntax: function(config) {
+ return createSyntax(mix({}, config));
+ },
+ fork: function(extension) {
+ var base = mix({}, config); // copy of config
+ return createSyntax(
+ typeof extension === 'function'
+ ? extension(base, Object.assign)
+ : mix(base, extension)
+ );
+ }
+ };
+
+ syntax.lexer = new Lexer({
+ generic: true,
+ types: config.types,
+ atrules: config.atrules,
+ properties: config.properties,
+ node: config.node
+ }, syntax);
+
+ return syntax;
+};
+
+exports.create = function(config) {
+ return createSyntax(mix({}, config));
+};
+
+
+/***/ }),
+/* 15 */
+/***/ ((module) => {
+
+//
+// list
+// ┌──────┐
+// ┌──────────────┼─head │
+// │ │ tail─┼──────────────┐
+// │ └──────┘ │
+// ▼ ▼
+// item item item item
+// ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
+// null ◀──┼─prev │◀───┼─prev │◀───┼─prev │◀───┼─prev │
+// │ next─┼───▶│ next─┼───▶│ next─┼───▶│ next─┼──▶ null
+// ├──────┤ ├──────┤ ├──────┤ ├──────┤
+// │ data │ │ data │ │ data │ │ data │
+// └──────┘ └──────┘ └──────┘ └──────┘
+//
+
+function createItem(data) {
+ return {
+ prev: null,
+ next: null,
+ data: data
+ };
+}
+
+function allocateCursor(node, prev, next) {
+ var cursor;
+
+ if (cursors !== null) {
+ cursor = cursors;
+ cursors = cursors.cursor;
+ cursor.prev = prev;
+ cursor.next = next;
+ cursor.cursor = node.cursor;
+ } else {
+ cursor = {
+ prev: prev,
+ next: next,
+ cursor: node.cursor
+ };
+ }
+
+ node.cursor = cursor;
+
+ return cursor;
+}
+
+function releaseCursor(node) {
+ var cursor = node.cursor;
+
+ node.cursor = cursor.cursor;
+ cursor.prev = null;
+ cursor.next = null;
+ cursor.cursor = cursors;
+ cursors = cursor;
+}
+
+var cursors = null;
+var List = function() {
+ this.cursor = null;
+ this.head = null;
+ this.tail = null;
+};
+
+List.createItem = createItem;
+List.prototype.createItem = createItem;
+
+List.prototype.updateCursors = function(prevOld, prevNew, nextOld, nextNew) {
+ var cursor = this.cursor;
+
+ while (cursor !== null) {
+ if (cursor.prev === prevOld) {
+ cursor.prev = prevNew;
+ }
+
+ if (cursor.next === nextOld) {
+ cursor.next = nextNew;
+ }
+
+ cursor = cursor.cursor;
+ }
+};
+
+List.prototype.getSize = function() {
+ var size = 0;
+ var cursor = this.head;
+
+ while (cursor) {
+ size++;
+ cursor = cursor.next;
+ }
+
+ return size;
+};
+
+List.prototype.fromArray = function(array) {
+ var cursor = null;
+
+ this.head = null;
+
+ for (var i = 0; i < array.length; i++) {
+ var item = createItem(array[i]);
+
+ if (cursor !== null) {
+ cursor.next = item;
+ } else {
+ this.head = item;
+ }
+
+ item.prev = cursor;
+ cursor = item;
+ }
+
+ this.tail = cursor;
+
+ return this;
+};
+
+List.prototype.toArray = function() {
+ var cursor = this.head;
+ var result = [];
+
+ while (cursor) {
+ result.push(cursor.data);
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.toJSON = List.prototype.toArray;
+
+List.prototype.isEmpty = function() {
+ return this.head === null;
+};
+
+List.prototype.first = function() {
+ return this.head && this.head.data;
+};
+
+List.prototype.last = function() {
+ return this.tail && this.tail.data;
+};
+
+List.prototype.each = function(fn, context) {
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, null, this.head);
+
+ while (cursor.next !== null) {
+ item = cursor.next;
+ cursor.next = item.next;
+
+ fn.call(context, item.data, item, this);
+ }
+
+ // pop cursor
+ releaseCursor(this);
+};
+
+List.prototype.forEach = List.prototype.each;
+
+List.prototype.eachRight = function(fn, context) {
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, this.tail, null);
+
+ while (cursor.prev !== null) {
+ item = cursor.prev;
+ cursor.prev = item.prev;
+
+ fn.call(context, item.data, item, this);
+ }
+
+ // pop cursor
+ releaseCursor(this);
+};
+
+List.prototype.forEachRight = List.prototype.eachRight;
+
+List.prototype.reduce = function(fn, initialValue, context) {
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, null, this.head);
+ var acc = initialValue;
+
+ while (cursor.next !== null) {
+ item = cursor.next;
+ cursor.next = item.next;
+
+ acc = fn.call(context, acc, item.data, item, this);
+ }
+
+ // pop cursor
+ releaseCursor(this);
+
+ return acc;
+};
+
+List.prototype.reduceRight = function(fn, initialValue, context) {
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, this.tail, null);
+ var acc = initialValue;
+
+ while (cursor.prev !== null) {
+ item = cursor.prev;
+ cursor.prev = item.prev;
+
+ acc = fn.call(context, acc, item.data, item, this);
+ }
+
+ // pop cursor
+ releaseCursor(this);
+
+ return acc;
+};
+
+List.prototype.nextUntil = function(start, fn, context) {
+ if (start === null) {
+ return;
+ }
+
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, null, start);
+
+ while (cursor.next !== null) {
+ item = cursor.next;
+ cursor.next = item.next;
+
+ if (fn.call(context, item.data, item, this)) {
+ break;
+ }
+ }
+
+ // pop cursor
+ releaseCursor(this);
+};
+
+List.prototype.prevUntil = function(start, fn, context) {
+ if (start === null) {
+ return;
+ }
+
+ var item;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ var cursor = allocateCursor(this, start, null);
+
+ while (cursor.prev !== null) {
+ item = cursor.prev;
+ cursor.prev = item.prev;
+
+ if (fn.call(context, item.data, item, this)) {
+ break;
+ }
+ }
+
+ // pop cursor
+ releaseCursor(this);
+};
+
+List.prototype.some = function(fn, context) {
+ var cursor = this.head;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ while (cursor !== null) {
+ if (fn.call(context, cursor.data, cursor, this)) {
+ return true;
+ }
+
+ cursor = cursor.next;
+ }
+
+ return false;
+};
+
+List.prototype.map = function(fn, context) {
+ var result = new List();
+ var cursor = this.head;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ while (cursor !== null) {
+ result.appendData(fn.call(context, cursor.data, cursor, this));
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.filter = function(fn, context) {
+ var result = new List();
+ var cursor = this.head;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ while (cursor !== null) {
+ if (fn.call(context, cursor.data, cursor, this)) {
+ result.appendData(cursor.data);
+ }
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.clear = function() {
+ this.head = null;
+ this.tail = null;
+};
+
+List.prototype.copy = function() {
+ var result = new List();
+ var cursor = this.head;
+
+ while (cursor !== null) {
+ result.insert(createItem(cursor.data));
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.prepend = function(item) {
+ // head
+ // ^
+ // item
+ this.updateCursors(null, item, this.head, item);
+
+ // insert to the beginning of the list
+ if (this.head !== null) {
+ // new item <- first item
+ this.head.prev = item;
+
+ // new item -> first item
+ item.next = this.head;
+ } else {
+ // if list has no head, then it also has no tail
+ // in this case tail points to the new item
+ this.tail = item;
+ }
+
+ // head always points to new item
+ this.head = item;
+
+ return this;
+};
+
+List.prototype.prependData = function(data) {
+ return this.prepend(createItem(data));
+};
+
+List.prototype.append = function(item) {
+ return this.insert(item);
+};
+
+List.prototype.appendData = function(data) {
+ return this.insert(createItem(data));
+};
+
+List.prototype.insert = function(item, before) {
+ if (before !== undefined && before !== null) {
+ // prev before
+ // ^
+ // item
+ this.updateCursors(before.prev, item, before, item);
+
+ if (before.prev === null) {
+ // insert to the beginning of list
+ if (this.head !== before) {
+ throw new Error('before doesn\'t belong to list');
+ }
+
+ // since head points to before therefore list doesn't empty
+ // no need to check tail
+ this.head = item;
+ before.prev = item;
+ item.next = before;
+
+ this.updateCursors(null, item);
+ } else {
+
+ // insert between two items
+ before.prev.next = item;
+ item.prev = before.prev;
+
+ before.prev = item;
+ item.next = before;
+ }
+ } else {
+ // tail
+ // ^
+ // item
+ this.updateCursors(this.tail, item, null, item);
+
+ // insert to the ending of the list
+ if (this.tail !== null) {
+ // last item -> new item
+ this.tail.next = item;
+
+ // last item <- new item
+ item.prev = this.tail;
+ } else {
+ // if list has no tail, then it also has no head
+ // in this case head points to new item
+ this.head = item;
+ }
+
+ // tail always points to new item
+ this.tail = item;
+ }
+
+ return this;
+};
+
+List.prototype.insertData = function(data, before) {
+ return this.insert(createItem(data), before);
+};
+
+List.prototype.remove = function(item) {
+ // item
+ // ^
+ // prev next
+ this.updateCursors(item, item.prev, item, item.next);
+
+ if (item.prev !== null) {
+ item.prev.next = item.next;
+ } else {
+ if (this.head !== item) {
+ throw new Error('item doesn\'t belong to list');
+ }
+
+ this.head = item.next;
+ }
+
+ if (item.next !== null) {
+ item.next.prev = item.prev;
+ } else {
+ if (this.tail !== item) {
+ throw new Error('item doesn\'t belong to list');
+ }
+
+ this.tail = item.prev;
+ }
+
+ item.prev = null;
+ item.next = null;
+
+ return item;
+};
+
+List.prototype.push = function(data) {
+ this.insert(createItem(data));
+};
+
+List.prototype.pop = function() {
+ if (this.tail !== null) {
+ return this.remove(this.tail);
+ }
+};
+
+List.prototype.unshift = function(data) {
+ this.prepend(createItem(data));
+};
+
+List.prototype.shift = function() {
+ if (this.head !== null) {
+ return this.remove(this.head);
+ }
+};
+
+List.prototype.prependList = function(list) {
+ return this.insertList(list, this.head);
+};
+
+List.prototype.appendList = function(list) {
+ return this.insertList(list);
+};
+
+List.prototype.insertList = function(list, before) {
+ // ignore empty lists
+ if (list.head === null) {
+ return this;
+ }
+
+ if (before !== undefined && before !== null) {
+ this.updateCursors(before.prev, list.tail, before, list.head);
+
+ // insert in the middle of dist list
+ if (before.prev !== null) {
+ // before.prev <-> list.head
+ before.prev.next = list.head;
+ list.head.prev = before.prev;
+ } else {
+ this.head = list.head;
+ }
+
+ before.prev = list.tail;
+ list.tail.next = before;
+ } else {
+ this.updateCursors(this.tail, list.tail, null, list.head);
+
+ // insert to end of the list
+ if (this.tail !== null) {
+ // if destination list has a tail, then it also has a head,
+ // but head doesn't change
+
+ // dest tail -> source head
+ this.tail.next = list.head;
+
+ // dest tail <- source head
+ list.head.prev = this.tail;
+ } else {
+ // if list has no a tail, then it also has no a head
+ // in this case points head to new item
+ this.head = list.head;
+ }
+
+ // tail always start point to new item
+ this.tail = list.tail;
+ }
+
+ list.head = null;
+ list.tail = null;
+
+ return this;
+};
+
+List.prototype.replace = function(oldItem, newItemOrList) {
+ if ('head' in newItemOrList) {
+ this.insertList(newItemOrList, oldItem);
+ } else {
+ this.insert(newItemOrList, oldItem);
+ }
+
+ this.remove(oldItem);
+};
+
+module.exports = List;
+
+
+/***/ }),
+/* 16 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+var createCustomError = __webpack_require__(17);
+var MAX_LINE_LENGTH = 100;
+var OFFSET_CORRECTION = 60;
+var TAB_REPLACEMENT = ' ';
+
+function sourceFragment(error, extraLines) {
+ function processLines(start, end) {
+ return lines.slice(start, end).map(function(line, idx) {
+ var num = String(start + idx + 1);
+
+ while (num.length < maxNumLength) {
+ num = ' ' + num;
+ }
+
+ return num + ' |' + line;
+ }).join('\n');
+ }
+
+ var lines = error.source.split(/\r\n?|\n|\f/);
+ var line = error.line;
+ var column = error.column;
+ var startLine = Math.max(1, line - extraLines) - 1;
+ var endLine = Math.min(line + extraLines, lines.length + 1);
+ var maxNumLength = Math.max(4, String(endLine).length) + 1;
+ var cutLeft = 0;
+
+ // column correction according to replaced tab before column
+ column += (TAB_REPLACEMENT.length - 1) * (lines[line - 1].substr(0, column - 1).match(/\t/g) || []).length;
+
+ if (column > MAX_LINE_LENGTH) {
+ cutLeft = column - OFFSET_CORRECTION + 3;
+ column = OFFSET_CORRECTION - 2;
+ }
+
+ for (var i = startLine; i <= endLine; i++) {
+ if (i >= 0 && i < lines.length) {
+ lines[i] = lines[i].replace(/\t/g, TAB_REPLACEMENT);
+ lines[i] =
+ (cutLeft > 0 && lines[i].length > cutLeft ? '\u2026' : '') +
+ lines[i].substr(cutLeft, MAX_LINE_LENGTH - 2) +
+ (lines[i].length > cutLeft + MAX_LINE_LENGTH - 1 ? '\u2026' : '');
+ }
+ }
+
+ return [
+ processLines(startLine, line),
+ new Array(column + maxNumLength + 2).join('-') + '^',
+ processLines(line, endLine)
+ ].filter(Boolean).join('\n');
+}
+
+var SyntaxError = function(message, source, offset, line, column) {
+ var error = createCustomError('SyntaxError', message);
+
+ error.source = source;
+ error.offset = offset;
+ error.line = line;
+ error.column = column;
+
+ error.sourceFragment = function(extraLines) {
+ return sourceFragment(error, isNaN(extraLines) ? 0 : extraLines);
+ };
+ Object.defineProperty(error, 'formattedMessage', {
+ get: function() {
+ return (
+ 'Parse error: ' + error.message + '\n' +
+ sourceFragment(error, 2)
+ );
+ }
+ });
+
+ // for backward capability
+ error.parseError = {
+ offset: offset,
+ line: line,
+ column: column
+ };
+
+ return error;
+};
+
+module.exports = SyntaxError;
+
+
+/***/ }),
+/* 17 */
+/***/ ((module) => {
+
+module.exports = function createCustomError(name, message) {
+ // use Object.create(), because some VMs prevent setting line/column otherwise
+ // (iOS Safari 10 even throws an exception)
+ var error = Object.create(SyntaxError.prototype);
+ var errorStack = new Error();
+
+ error.name = name;
+ error.message = message;
+
+ Object.defineProperty(error, 'stack', {
+ get: function() {
+ return (errorStack.stack || '').replace(/^(.+\n){1,3}/, name + ': ' + message + '\n');
+ }
+ });
+
+ return error;
+};
+
+
+/***/ }),
+/* 18 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+var constants = __webpack_require__(19);
+var TYPE = constants.TYPE;
+var NAME = constants.NAME;
+
+var utils = __webpack_require__(20);
+var cmpStr = utils.cmpStr;
+
+var EOF = TYPE.EOF;
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+
+var OFFSET_MASK = 0x00FFFFFF;
+var TYPE_SHIFT = 24;
+
+var TokenStream = function() {
+ this.offsetAndType = null;
+ this.balance = null;
+
+ this.reset();
+};
+
+TokenStream.prototype = {
+ reset: function() {
+ this.eof = false;
+ this.tokenIndex = -1;
+ this.tokenType = 0;
+ this.tokenStart = this.firstCharOffset;
+ this.tokenEnd = this.firstCharOffset;
+ },
+
+ lookupType: function(offset) {
+ offset += this.tokenIndex;
+
+ if (offset < this.tokenCount) {
+ return this.offsetAndType[offset] >> TYPE_SHIFT;
+ }
+
+ return EOF;
+ },
+ lookupOffset: function(offset) {
+ offset += this.tokenIndex;
+
+ if (offset < this.tokenCount) {
+ return this.offsetAndType[offset - 1] & OFFSET_MASK;
+ }
+
+ return this.source.length;
+ },
+ lookupValue: function(offset, referenceStr) {
+ offset += this.tokenIndex;
+
+ if (offset < this.tokenCount) {
+ return cmpStr(
+ this.source,
+ this.offsetAndType[offset - 1] & OFFSET_MASK,
+ this.offsetAndType[offset] & OFFSET_MASK,
+ referenceStr
+ );
+ }
+
+ return false;
+ },
+ getTokenStart: function(tokenIndex) {
+ if (tokenIndex === this.tokenIndex) {
+ return this.tokenStart;
+ }
+
+ if (tokenIndex > 0) {
+ return tokenIndex < this.tokenCount
+ ? this.offsetAndType[tokenIndex - 1] & OFFSET_MASK
+ : this.offsetAndType[this.tokenCount] & OFFSET_MASK;
+ }
+
+ return this.firstCharOffset;
+ },
+
+ // TODO: -> skipUntilBalanced
+ getRawLength: function(startToken, mode) {
+ var cursor = startToken;
+ var balanceEnd;
+ var offset = this.offsetAndType[Math.max(cursor - 1, 0)] & OFFSET_MASK;
+ var type;
+
+ loop:
+ for (; cursor < this.tokenCount; cursor++) {
+ balanceEnd = this.balance[cursor];
+
+ // stop scanning on balance edge that points to offset before start token
+ if (balanceEnd < startToken) {
+ break loop;
+ }
+
+ type = this.offsetAndType[cursor] >> TYPE_SHIFT;
+
+ // check token is stop type
+ switch (mode(type, this.source, offset)) {
+ case 1:
+ break loop;
+
+ case 2:
+ cursor++;
+ break loop;
+
+ default:
+ // fast forward to the end of balanced block
+ if (this.balance[balanceEnd] === cursor) {
+ cursor = balanceEnd;
+ }
+
+ offset = this.offsetAndType[cursor] & OFFSET_MASK;
+ }
+ }
+
+ return cursor - this.tokenIndex;
+ },
+ isBalanceEdge: function(pos) {
+ return this.balance[this.tokenIndex] < pos;
+ },
+ isDelim: function(code, offset) {
+ if (offset) {
+ return (
+ this.lookupType(offset) === TYPE.Delim &&
+ this.source.charCodeAt(this.lookupOffset(offset)) === code
+ );
+ }
+
+ return (
+ this.tokenType === TYPE.Delim &&
+ this.source.charCodeAt(this.tokenStart) === code
+ );
+ },
+
+ getTokenValue: function() {
+ return this.source.substring(this.tokenStart, this.tokenEnd);
+ },
+ getTokenLength: function() {
+ return this.tokenEnd - this.tokenStart;
+ },
+ substrToCursor: function(start) {
+ return this.source.substring(start, this.tokenStart);
+ },
+
+ skipWS: function() {
+ for (var i = this.tokenIndex, skipTokenCount = 0; i < this.tokenCount; i++, skipTokenCount++) {
+ if ((this.offsetAndType[i] >> TYPE_SHIFT) !== WHITESPACE) {
+ break;
+ }
+ }
+
+ if (skipTokenCount > 0) {
+ this.skip(skipTokenCount);
+ }
+ },
+ skipSC: function() {
+ while (this.tokenType === WHITESPACE || this.tokenType === COMMENT) {
+ this.next();
+ }
+ },
+ skip: function(tokenCount) {
+ var next = this.tokenIndex + tokenCount;
+
+ if (next < this.tokenCount) {
+ this.tokenIndex = next;
+ this.tokenStart = this.offsetAndType[next - 1] & OFFSET_MASK;
+ next = this.offsetAndType[next];
+ this.tokenType = next >> TYPE_SHIFT;
+ this.tokenEnd = next & OFFSET_MASK;
+ } else {
+ this.tokenIndex = this.tokenCount;
+ this.next();
+ }
+ },
+ next: function() {
+ var next = this.tokenIndex + 1;
+
+ if (next < this.tokenCount) {
+ this.tokenIndex = next;
+ this.tokenStart = this.tokenEnd;
+ next = this.offsetAndType[next];
+ this.tokenType = next >> TYPE_SHIFT;
+ this.tokenEnd = next & OFFSET_MASK;
+ } else {
+ this.tokenIndex = this.tokenCount;
+ this.eof = true;
+ this.tokenType = EOF;
+ this.tokenStart = this.tokenEnd = this.source.length;
+ }
+ },
+
+ forEachToken(fn) {
+ for (var i = 0, offset = this.firstCharOffset; i < this.tokenCount; i++) {
+ var start = offset;
+ var item = this.offsetAndType[i];
+ var end = item & OFFSET_MASK;
+ var type = item >> TYPE_SHIFT;
+
+ offset = end;
+
+ fn(type, start, end, i);
+ }
+ },
+
+ dump() {
+ var tokens = new Array(this.tokenCount);
+
+ this.forEachToken((type, start, end, index) => {
+ tokens[index] = {
+ idx: index,
+ type: NAME[type],
+ chunk: this.source.substring(start, end),
+ balance: this.balance[index]
+ };
+ });
+
+ return tokens;
+ }
+};
+
+module.exports = TokenStream;
+
+
+/***/ }),
+/* 19 */
+/***/ ((module) => {
+
+// CSS Syntax Module Level 3
+// https://www.w3.org/TR/css-syntax-3/
+var TYPE = {
+ EOF: 0, //
+ Ident: 1, //
+ Function: 2, //
+ AtKeyword: 3, //
+ Hash: 4, //
+ String: 5, //
+ BadString: 6, //
+ Url: 7, //
+ BadUrl: 8, //
+ Delim: 9, //
+ Number: 10, //
+ Percentage: 11, //
+ Dimension: 12, //
+ WhiteSpace: 13, //
+ CDO: 14, //
+ CDC: 15, //
+ Colon: 16, // :
+ Semicolon: 17, // ;
+ Comma: 18, // ,
+ LeftSquareBracket: 19, // <[-token>
+ RightSquareBracket: 20, // <]-token>
+ LeftParenthesis: 21, // <(-token>
+ RightParenthesis: 22, // <)-token>
+ LeftCurlyBracket: 23, // <{-token>
+ RightCurlyBracket: 24, // <}-token>
+ Comment: 25
+};
+
+var NAME = Object.keys(TYPE).reduce(function(result, key) {
+ result[TYPE[key]] = key;
+ return result;
+}, {});
+
+module.exports = {
+ TYPE: TYPE,
+ NAME: NAME
+};
+
+
+/***/ }),
+/* 20 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+var charCodeDef = __webpack_require__(21);
+var isDigit = charCodeDef.isDigit;
+var isHexDigit = charCodeDef.isHexDigit;
+var isUppercaseLetter = charCodeDef.isUppercaseLetter;
+var isName = charCodeDef.isName;
+var isWhiteSpace = charCodeDef.isWhiteSpace;
+var isValidEscape = charCodeDef.isValidEscape;
+
+function getCharCode(source, offset) {
+ return offset < source.length ? source.charCodeAt(offset) : 0;
+}
+
+function getNewlineLength(source, offset, code) {
+ if (code === 13 /* \r */ && getCharCode(source, offset + 1) === 10 /* \n */) {
+ return 2;
+ }
+
+ return 1;
+}
+
+function cmpChar(testStr, offset, referenceCode) {
+ var code = testStr.charCodeAt(offset);
+
+ // code.toLowerCase() for A..Z
+ if (isUppercaseLetter(code)) {
+ code = code | 32;
+ }
+
+ return code === referenceCode;
+}
+
+function cmpStr(testStr, start, end, referenceStr) {
+ if (end - start !== referenceStr.length) {
+ return false;
+ }
+
+ if (start < 0 || end > testStr.length) {
+ return false;
+ }
+
+ for (var i = start; i < end; i++) {
+ var testCode = testStr.charCodeAt(i);
+ var referenceCode = referenceStr.charCodeAt(i - start);
+
+ // testCode.toLowerCase() for A..Z
+ if (isUppercaseLetter(testCode)) {
+ testCode = testCode | 32;
+ }
+
+ if (testCode !== referenceCode) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function findWhiteSpaceStart(source, offset) {
+ for (; offset >= 0; offset--) {
+ if (!isWhiteSpace(source.charCodeAt(offset))) {
+ break;
+ }
+ }
+
+ return offset + 1;
+}
+
+function findWhiteSpaceEnd(source, offset) {
+ for (; offset < source.length; offset++) {
+ if (!isWhiteSpace(source.charCodeAt(offset))) {
+ break;
+ }
+ }
+
+ return offset;
+}
+
+function findDecimalNumberEnd(source, offset) {
+ for (; offset < source.length; offset++) {
+ if (!isDigit(source.charCodeAt(offset))) {
+ break;
+ }
+ }
+
+ return offset;
+}
+
+// § 4.3.7. Consume an escaped code point
+function consumeEscaped(source, offset) {
+ // It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and
+ // that the next input code point has already been verified to be part of a valid escape.
+ offset += 2;
+
+ // hex digit
+ if (isHexDigit(getCharCode(source, offset - 1))) {
+ // Consume as many hex digits as possible, but no more than 5.
+ // Note that this means 1-6 hex digits have been consumed in total.
+ for (var maxOffset = Math.min(source.length, offset + 5); offset < maxOffset; offset++) {
+ if (!isHexDigit(getCharCode(source, offset))) {
+ break;
+ }
+ }
+
+ // If the next input code point is whitespace, consume it as well.
+ var code = getCharCode(source, offset);
+ if (isWhiteSpace(code)) {
+ offset += getNewlineLength(source, offset, code);
+ }
+ }
+
+ return offset;
+}
+
+// §4.3.11. Consume a name
+// Note: This algorithm does not do the verification of the first few code points that are necessary
+// to ensure the returned code points would constitute an . If that is the intended use,
+// ensure that the stream starts with an identifier before calling this algorithm.
+function consumeName(source, offset) {
+ // Let result initially be an empty string.
+ // Repeatedly consume the next input code point from the stream:
+ for (; offset < source.length; offset++) {
+ var code = source.charCodeAt(offset);
+
+ // name code point
+ if (isName(code)) {
+ // Append the code point to result.
+ continue;
+ }
+
+ // the stream starts with a valid escape
+ if (isValidEscape(code, getCharCode(source, offset + 1))) {
+ // Consume an escaped code point. Append the returned code point to result.
+ offset = consumeEscaped(source, offset) - 1;
+ continue;
+ }
+
+ // anything else
+ // Reconsume the current input code point. Return result.
+ break;
+ }
+
+ return offset;
+}
+
+// §4.3.12. Consume a number
+function consumeNumber(source, offset) {
+ var code = source.charCodeAt(offset);
+
+ // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-),
+ // consume it and append it to repr.
+ if (code === 0x002B || code === 0x002D) {
+ code = source.charCodeAt(offset += 1);
+ }
+
+ // 3. While the next input code point is a digit, consume it and append it to repr.
+ if (isDigit(code)) {
+ offset = findDecimalNumberEnd(source, offset + 1);
+ code = source.charCodeAt(offset);
+ }
+
+ // 4. If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
+ if (code === 0x002E && isDigit(source.charCodeAt(offset + 1))) {
+ // 4.1 Consume them.
+ // 4.2 Append them to repr.
+ code = source.charCodeAt(offset += 2);
+
+ // 4.3 Set type to "number".
+ // TODO
+
+ // 4.4 While the next input code point is a digit, consume it and append it to repr.
+
+ offset = findDecimalNumberEnd(source, offset);
+ }
+
+ // 5. If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E)
+ // or U+0065 LATIN SMALL LETTER E (e), ... , followed by a digit, then:
+ if (cmpChar(source, offset, 101 /* e */)) {
+ var sign = 0;
+ code = source.charCodeAt(offset + 1);
+
+ // ... optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+) ...
+ if (code === 0x002D || code === 0x002B) {
+ sign = 1;
+ code = source.charCodeAt(offset + 2);
+ }
+
+ // ... followed by a digit
+ if (isDigit(code)) {
+ // 5.1 Consume them.
+ // 5.2 Append them to repr.
+
+ // 5.3 Set type to "number".
+ // TODO
+
+ // 5.4 While the next input code point is a digit, consume it and append it to repr.
+ offset = findDecimalNumberEnd(source, offset + 1 + sign + 1);
+ }
+ }
+
+ return offset;
+}
+
+// § 4.3.14. Consume the remnants of a bad url
+// ... its sole use is to consume enough of the input stream to reach a recovery point
+// where normal tokenizing can resume.
+function consumeBadUrlRemnants(source, offset) {
+ // Repeatedly consume the next input code point from the stream:
+ for (; offset < source.length; offset++) {
+ var code = source.charCodeAt(offset);
+
+ // U+0029 RIGHT PARENTHESIS ())
+ // EOF
+ if (code === 0x0029) {
+ // Return.
+ offset++;
+ break;
+ }
+
+ if (isValidEscape(code, getCharCode(source, offset + 1))) {
+ // Consume an escaped code point.
+ // Note: This allows an escaped right parenthesis ("\)") to be encountered
+ // without ending the . This is otherwise identical to
+ // the "anything else" clause.
+ offset = consumeEscaped(source, offset);
+ }
+ }
+
+ return offset;
+}
+
+module.exports = {
+ consumeEscaped: consumeEscaped,
+ consumeName: consumeName,
+ consumeNumber: consumeNumber,
+ consumeBadUrlRemnants: consumeBadUrlRemnants,
+
+ cmpChar: cmpChar,
+ cmpStr: cmpStr,
+
+ getNewlineLength: getNewlineLength,
+ findWhiteSpaceStart: findWhiteSpaceStart,
+ findWhiteSpaceEnd: findWhiteSpaceEnd
+};
+
+
+/***/ }),
+/* 21 */
+/***/ ((module) => {
+
+var EOF = 0;
+
+// https://drafts.csswg.org/css-syntax-3/
+// § 4.2. Definitions
+
+// digit
+// A code point between U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9).
+function isDigit(code) {
+ return code >= 0x0030 && code <= 0x0039;
+}
+
+// hex digit
+// A digit, or a code point between U+0041 LATIN CAPITAL LETTER A (A) and U+0046 LATIN CAPITAL LETTER F (F),
+// or a code point between U+0061 LATIN SMALL LETTER A (a) and U+0066 LATIN SMALL LETTER F (f).
+function isHexDigit(code) {
+ return (
+ isDigit(code) || // 0 .. 9
+ (code >= 0x0041 && code <= 0x0046) || // A .. F
+ (code >= 0x0061 && code <= 0x0066) // a .. f
+ );
+}
+
+// uppercase letter
+// A code point between U+0041 LATIN CAPITAL LETTER A (A) and U+005A LATIN CAPITAL LETTER Z (Z).
+function isUppercaseLetter(code) {
+ return code >= 0x0041 && code <= 0x005A;
+}
+
+// lowercase letter
+// A code point between U+0061 LATIN SMALL LETTER A (a) and U+007A LATIN SMALL LETTER Z (z).
+function isLowercaseLetter(code) {
+ return code >= 0x0061 && code <= 0x007A;
+}
+
+// letter
+// An uppercase letter or a lowercase letter.
+function isLetter(code) {
+ return isUppercaseLetter(code) || isLowercaseLetter(code);
+}
+
+// non-ASCII code point
+// A code point with a value equal to or greater than U+0080 .
+function isNonAscii(code) {
+ return code >= 0x0080;
+}
+
+// name-start code point
+// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
+function isNameStart(code) {
+ return isLetter(code) || isNonAscii(code) || code === 0x005F;
+}
+
+// name code point
+// A name-start code point, a digit, or U+002D HYPHEN-MINUS (-).
+function isName(code) {
+ return isNameStart(code) || isDigit(code) || code === 0x002D;
+}
+
+// non-printable code point
+// A code point between U+0000 NULL and U+0008 BACKSPACE, or U+000B LINE TABULATION,
+// or a code point between U+000E SHIFT OUT and U+001F INFORMATION SEPARATOR ONE, or U+007F DELETE.
+function isNonPrintable(code) {
+ return (
+ (code >= 0x0000 && code <= 0x0008) ||
+ (code === 0x000B) ||
+ (code >= 0x000E && code <= 0x001F) ||
+ (code === 0x007F)
+ );
+}
+
+// newline
+// U+000A LINE FEED. Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are not included in this definition,
+// as they are converted to U+000A LINE FEED during preprocessing.
+// TODO: we doesn't do a preprocessing, so check a code point for U+000D CARRIAGE RETURN and U+000C FORM FEED
+function isNewline(code) {
+ return code === 0x000A || code === 0x000D || code === 0x000C;
+}
+
+// whitespace
+// A newline, U+0009 CHARACTER TABULATION, or U+0020 SPACE.
+function isWhiteSpace(code) {
+ return isNewline(code) || code === 0x0020 || code === 0x0009;
+}
+
+// § 4.3.8. Check if two code points are a valid escape
+function isValidEscape(first, second) {
+ // If the first code point is not U+005C REVERSE SOLIDUS (\), return false.
+ if (first !== 0x005C) {
+ return false;
+ }
+
+ // Otherwise, if the second code point is a newline or EOF, return false.
+ if (isNewline(second) || second === EOF) {
+ return false;
+ }
+
+ // Otherwise, return true.
+ return true;
+}
+
+// § 4.3.9. Check if three code points would start an identifier
+function isIdentifierStart(first, second, third) {
+ // Look at the first code point:
+
+ // U+002D HYPHEN-MINUS
+ if (first === 0x002D) {
+ // If the second code point is a name-start code point or a U+002D HYPHEN-MINUS,
+ // or the second and third code points are a valid escape, return true. Otherwise, return false.
+ return (
+ isNameStart(second) ||
+ second === 0x002D ||
+ isValidEscape(second, third)
+ );
+ }
+
+ // name-start code point
+ if (isNameStart(first)) {
+ // Return true.
+ return true;
+ }
+
+ // U+005C REVERSE SOLIDUS (\)
+ if (first === 0x005C) {
+ // If the first and second code points are a valid escape, return true. Otherwise, return false.
+ return isValidEscape(first, second);
+ }
+
+ // anything else
+ // Return false.
+ return false;
+}
+
+// § 4.3.10. Check if three code points would start a number
+function isNumberStart(first, second, third) {
+ // Look at the first code point:
+
+ // U+002B PLUS SIGN (+)
+ // U+002D HYPHEN-MINUS (-)
+ if (first === 0x002B || first === 0x002D) {
+ // If the second code point is a digit, return true.
+ if (isDigit(second)) {
+ return 2;
+ }
+
+ // Otherwise, if the second code point is a U+002E FULL STOP (.)
+ // and the third code point is a digit, return true.
+ // Otherwise, return false.
+ return second === 0x002E && isDigit(third) ? 3 : 0;
+ }
+
+ // U+002E FULL STOP (.)
+ if (first === 0x002E) {
+ // If the second code point is a digit, return true. Otherwise, return false.
+ return isDigit(second) ? 2 : 0;
+ }
+
+ // digit
+ if (isDigit(first)) {
+ // Return true.
+ return 1;
+ }
+
+ // anything else
+ // Return false.
+ return 0;
+}
+
+//
+// Misc
+//
+
+// detect BOM (https://en.wikipedia.org/wiki/Byte_order_mark)
+function isBOM(code) {
+ // UTF-16BE
+ if (code === 0xFEFF) {
+ return 1;
+ }
+
+ // UTF-16LE
+ if (code === 0xFFFE) {
+ return 1;
+ }
+
+ return 0;
+}
+
+// Fast code category
+//
+// https://drafts.csswg.org/css-syntax/#tokenizer-definitions
+// > non-ASCII code point
+// > A code point with a value equal to or greater than U+0080
+// > name-start code point
+// > A letter, a non-ASCII code point, or U+005F LOW LINE (_).
+// > name code point
+// > A name-start code point, a digit, or U+002D HYPHEN-MINUS (-)
+// That means only ASCII code points has a special meaning and we define a maps for 0..127 codes only
+var CATEGORY = new Array(0x80);
+charCodeCategory.Eof = 0x80;
+charCodeCategory.WhiteSpace = 0x82;
+charCodeCategory.Digit = 0x83;
+charCodeCategory.NameStart = 0x84;
+charCodeCategory.NonPrintable = 0x85;
+
+for (var i = 0; i < CATEGORY.length; i++) {
+ switch (true) {
+ case isWhiteSpace(i):
+ CATEGORY[i] = charCodeCategory.WhiteSpace;
+ break;
+
+ case isDigit(i):
+ CATEGORY[i] = charCodeCategory.Digit;
+ break;
+
+ case isNameStart(i):
+ CATEGORY[i] = charCodeCategory.NameStart;
+ break;
+
+ case isNonPrintable(i):
+ CATEGORY[i] = charCodeCategory.NonPrintable;
+ break;
+
+ default:
+ CATEGORY[i] = i || charCodeCategory.Eof;
+ }
+}
+
+function charCodeCategory(code) {
+ return code < 0x80 ? CATEGORY[code] : charCodeCategory.NameStart;
+};
+
+module.exports = {
+ isDigit: isDigit,
+ isHexDigit: isHexDigit,
+ isUppercaseLetter: isUppercaseLetter,
+ isLowercaseLetter: isLowercaseLetter,
+ isLetter: isLetter,
+ isNonAscii: isNonAscii,
+ isNameStart: isNameStart,
+ isName: isName,
+ isNonPrintable: isNonPrintable,
+ isNewline: isNewline,
+ isWhiteSpace: isWhiteSpace,
+ isValidEscape: isValidEscape,
+ isIdentifierStart: isIdentifierStart,
+ isNumberStart: isNumberStart,
+
+ isBOM: isBOM,
+ charCodeCategory: charCodeCategory
+};
+
+
+/***/ }),
+/* 22 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+var SyntaxReferenceError = __webpack_require__(23).SyntaxReferenceError;
+var SyntaxMatchError = __webpack_require__(23).SyntaxMatchError;
+var names = __webpack_require__(25);
+var generic = __webpack_require__(26);
+var parse = __webpack_require__(31);
+var generate = __webpack_require__(24);
+var walk = __webpack_require__(34);
+var prepareTokens = __webpack_require__(35);
+var buildMatchGraph = __webpack_require__(36).buildMatchGraph;
+var matchAsTree = __webpack_require__(37).matchAsTree;
+var trace = __webpack_require__(38);
+var search = __webpack_require__(39);
+var getStructureFromConfig = __webpack_require__(40).getStructureFromConfig;
+var cssWideKeywords = buildMatchGraph('inherit | initial | unset');
+var cssWideKeywordsWithExpression = buildMatchGraph('inherit | initial | unset | <-ms-legacy-expression>');
+
+function dumpMapSyntax(map, compact, syntaxAsAst) {
+ var result = {};
+
+ for (var name in map) {
+ if (map[name].syntax) {
+ result[name] = syntaxAsAst
+ ? map[name].syntax
+ : generate(map[name].syntax, { compact: compact });
+ }
+ }
+
+ return result;
+}
+
+function dumpAtruleMapSyntax(map, compact, syntaxAsAst) {
+ const result = {};
+
+ for (const [name, atrule] of Object.entries(map)) {
+ result[name] = {
+ prelude: atrule.prelude && (
+ syntaxAsAst
+ ? atrule.prelude.syntax
+ : generate(atrule.prelude.syntax, { compact })
+ ),
+ descriptors: atrule.descriptors && dumpMapSyntax(atrule.descriptors, compact, syntaxAsAst)
+ };
+ }
+
+ return result;
+}
+
+function valueHasVar(tokens) {
+ for (var i = 0; i < tokens.length; i++) {
+ if (tokens[i].value.toLowerCase() === 'var(') {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function buildMatchResult(match, error, iterations) {
+ return {
+ matched: match,
+ iterations: iterations,
+ error: error,
+ getTrace: trace.getTrace,
+ isType: trace.isType,
+ isProperty: trace.isProperty,
+ isKeyword: trace.isKeyword
+ };
+}
+
+function matchSyntax(lexer, syntax, value, useCommon) {
+ var tokens = prepareTokens(value, lexer.syntax);
+ var result;
+
+ if (valueHasVar(tokens)) {
+ return buildMatchResult(null, new Error('Matching for a tree with var() is not supported'));
+ }
+
+ if (useCommon) {
+ result = matchAsTree(tokens, lexer.valueCommonSyntax, lexer);
+ }
+
+ if (!useCommon || !result.match) {
+ result = matchAsTree(tokens, syntax.match, lexer);
+ if (!result.match) {
+ return buildMatchResult(
+ null,
+ new SyntaxMatchError(result.reason, syntax.syntax, value, result),
+ result.iterations
+ );
+ }
+ }
+
+ return buildMatchResult(result.match, null, result.iterations);
+}
+
+var Lexer = function(config, syntax, structure) {
+ this.valueCommonSyntax = cssWideKeywords;
+ this.syntax = syntax;
+ this.generic = false;
+ this.atrules = {};
+ this.properties = {};
+ this.types = {};
+ this.structure = structure || getStructureFromConfig(config);
+
+ if (config) {
+ if (config.types) {
+ for (var name in config.types) {
+ this.addType_(name, config.types[name]);
+ }
+ }
+
+ if (config.generic) {
+ this.generic = true;
+ for (var name in generic) {
+ this.addType_(name, generic[name]);
+ }
+ }
+
+ if (config.atrules) {
+ for (var name in config.atrules) {
+ this.addAtrule_(name, config.atrules[name]);
+ }
+ }
+
+ if (config.properties) {
+ for (var name in config.properties) {
+ this.addProperty_(name, config.properties[name]);
+ }
+ }
+ }
+};
+
+Lexer.prototype = {
+ structure: {},
+ checkStructure: function(ast) {
+ function collectWarning(node, message) {
+ warns.push({
+ node: node,
+ message: message
+ });
+ }
+
+ var structure = this.structure;
+ var warns = [];
+
+ this.syntax.walk(ast, function(node) {
+ if (structure.hasOwnProperty(node.type)) {
+ structure[node.type].check(node, collectWarning);
+ } else {
+ collectWarning(node, 'Unknown node type `' + node.type + '`');
+ }
+ });
+
+ return warns.length ? warns : false;
+ },
+
+ createDescriptor: function(syntax, type, name, parent = null) {
+ var ref = {
+ type: type,
+ name: name
+ };
+ var descriptor = {
+ type: type,
+ name: name,
+ parent: parent,
+ syntax: null,
+ match: null
+ };
+
+ if (typeof syntax === 'function') {
+ descriptor.match = buildMatchGraph(syntax, ref);
+ } else {
+ if (typeof syntax === 'string') {
+ // lazy parsing on first access
+ Object.defineProperty(descriptor, 'syntax', {
+ get: function() {
+ Object.defineProperty(descriptor, 'syntax', {
+ value: parse(syntax)
+ });
+
+ return descriptor.syntax;
+ }
+ });
+ } else {
+ descriptor.syntax = syntax;
+ }
+
+ // lazy graph build on first access
+ Object.defineProperty(descriptor, 'match', {
+ get: function() {
+ Object.defineProperty(descriptor, 'match', {
+ value: buildMatchGraph(descriptor.syntax, ref)
+ });
+
+ return descriptor.match;
+ }
+ });
+ }
+
+ return descriptor;
+ },
+ addAtrule_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.atrules[name] = {
+ type: 'Atrule',
+ name: name,
+ prelude: syntax.prelude ? this.createDescriptor(syntax.prelude, 'AtrulePrelude', name) : null,
+ descriptors: syntax.descriptors
+ ? Object.keys(syntax.descriptors).reduce((res, descName) => {
+ res[descName] = this.createDescriptor(syntax.descriptors[descName], 'AtruleDescriptor', descName, name);
+ return res;
+ }, {})
+ : null
+ };
+ },
+ addProperty_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.properties[name] = this.createDescriptor(syntax, 'Property', name);
+ },
+ addType_: function(name, syntax) {
+ if (!syntax) {
+ return;
+ }
+
+ this.types[name] = this.createDescriptor(syntax, 'Type', name);
+
+ if (syntax === generic['-ms-legacy-expression']) {
+ this.valueCommonSyntax = cssWideKeywordsWithExpression;
+ }
+ },
+
+ checkAtruleName: function(atruleName) {
+ if (!this.getAtrule(atruleName)) {
+ return new SyntaxReferenceError('Unknown at-rule', '@' + atruleName);
+ }
+ },
+ checkAtrulePrelude: function(atruleName, prelude) {
+ let error = this.checkAtruleName(atruleName);
+
+ if (error) {
+ return error;
+ }
+
+ var atrule = this.getAtrule(atruleName);
+
+ if (!atrule.prelude && prelude) {
+ return new SyntaxError('At-rule `@' + atruleName + '` should not contain a prelude');
+ }
+
+ if (atrule.prelude && !prelude) {
+ return new SyntaxError('At-rule `@' + atruleName + '` should contain a prelude');
+ }
+ },
+ checkAtruleDescriptorName: function(atruleName, descriptorName) {
+ let error = this.checkAtruleName(atruleName);
+
+ if (error) {
+ return error;
+ }
+
+ var atrule = this.getAtrule(atruleName);
+ var descriptor = names.keyword(descriptorName);
+
+ if (!atrule.descriptors) {
+ return new SyntaxError('At-rule `@' + atruleName + '` has no known descriptors');
+ }
+
+ if (!atrule.descriptors[descriptor.name] &&
+ !atrule.descriptors[descriptor.basename]) {
+ return new SyntaxReferenceError('Unknown at-rule descriptor', descriptorName);
+ }
+ },
+ checkPropertyName: function(propertyName) {
+ var property = names.property(propertyName);
+
+ // don't match syntax for a custom property
+ if (property.custom) {
+ return new Error('Lexer matching doesn\'t applicable for custom properties');
+ }
+
+ if (!this.getProperty(propertyName)) {
+ return new SyntaxReferenceError('Unknown property', propertyName);
+ }
+ },
+
+ matchAtrulePrelude: function(atruleName, prelude) {
+ var error = this.checkAtrulePrelude(atruleName, prelude);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ if (!prelude) {
+ return buildMatchResult(null, null);
+ }
+
+ return matchSyntax(this, this.getAtrule(atruleName).prelude, prelude, false);
+ },
+ matchAtruleDescriptor: function(atruleName, descriptorName, value) {
+ var error = this.checkAtruleDescriptorName(atruleName, descriptorName);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ var atrule = this.getAtrule(atruleName);
+ var descriptor = names.keyword(descriptorName);
+
+ return matchSyntax(this, atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename], value, false);
+ },
+ matchDeclaration: function(node) {
+ if (node.type !== 'Declaration') {
+ return buildMatchResult(null, new Error('Not a Declaration node'));
+ }
+
+ return this.matchProperty(node.property, node.value);
+ },
+ matchProperty: function(propertyName, value) {
+ var error = this.checkPropertyName(propertyName);
+
+ if (error) {
+ return buildMatchResult(null, error);
+ }
+
+ return matchSyntax(this, this.getProperty(propertyName), value, true);
+ },
+ matchType: function(typeName, value) {
+ var typeSyntax = this.getType(typeName);
+
+ if (!typeSyntax) {
+ return buildMatchResult(null, new SyntaxReferenceError('Unknown type', typeName));
+ }
+
+ return matchSyntax(this, typeSyntax, value, false);
+ },
+ match: function(syntax, value) {
+ if (typeof syntax !== 'string' && (!syntax || !syntax.type)) {
+ return buildMatchResult(null, new SyntaxReferenceError('Bad syntax'));
+ }
+
+ if (typeof syntax === 'string' || !syntax.match) {
+ syntax = this.createDescriptor(syntax, 'Type', 'anonymous');
+ }
+
+ return matchSyntax(this, syntax, value, false);
+ },
+
+ findValueFragments: function(propertyName, value, type, name) {
+ return search.matchFragments(this, value, this.matchProperty(propertyName, value), type, name);
+ },
+ findDeclarationValueFragments: function(declaration, type, name) {
+ return search.matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name);
+ },
+ findAllFragments: function(ast, type, name) {
+ var result = [];
+
+ this.syntax.walk(ast, {
+ visit: 'Declaration',
+ enter: function(declaration) {
+ result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name));
+ }.bind(this)
+ });
+
+ return result;
+ },
+
+ getAtrule: function(atruleName, fallbackBasename = true) {
+ var atrule = names.keyword(atruleName);
+ var atruleEntry = atrule.vendor && fallbackBasename
+ ? this.atrules[atrule.name] || this.atrules[atrule.basename]
+ : this.atrules[atrule.name];
+
+ return atruleEntry || null;
+ },
+ getAtrulePrelude: function(atruleName, fallbackBasename = true) {
+ const atrule = this.getAtrule(atruleName, fallbackBasename);
+
+ return atrule && atrule.prelude || null;
+ },
+ getAtruleDescriptor: function(atruleName, name) {
+ return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
+ ? this.atrules[atruleName].declarators[name] || null
+ : null;
+ },
+ getProperty: function(propertyName, fallbackBasename = true) {
+ var property = names.property(propertyName);
+ var propertyEntry = property.vendor && fallbackBasename
+ ? this.properties[property.name] || this.properties[property.basename]
+ : this.properties[property.name];
+
+ return propertyEntry || null;
+ },
+ getType: function(name) {
+ return this.types.hasOwnProperty(name) ? this.types[name] : null;
+ },
+
+ validate: function() {
+ function validate(syntax, name, broken, descriptor) {
+ if (broken.hasOwnProperty(name)) {
+ return broken[name];
+ }
+
+ broken[name] = false;
+ if (descriptor.syntax !== null) {
+ walk(descriptor.syntax, function(node) {
+ if (node.type !== 'Type' && node.type !== 'Property') {
+ return;
+ }
+
+ var map = node.type === 'Type' ? syntax.types : syntax.properties;
+ var brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties;
+
+ if (!map.hasOwnProperty(node.name) || validate(syntax, node.name, brokenMap, map[node.name])) {
+ broken[name] = true;
+ }
+ }, this);
+ }
+ }
+
+ var brokenTypes = {};
+ var brokenProperties = {};
+
+ for (var key in this.types) {
+ validate(this, key, brokenTypes, this.types[key]);
+ }
+
+ for (var key in this.properties) {
+ validate(this, key, brokenProperties, this.properties[key]);
+ }
+
+ brokenTypes = Object.keys(brokenTypes).filter(function(name) {
+ return brokenTypes[name];
+ });
+ brokenProperties = Object.keys(brokenProperties).filter(function(name) {
+ return brokenProperties[name];
+ });
+
+ if (brokenTypes.length || brokenProperties.length) {
+ return {
+ types: brokenTypes,
+ properties: brokenProperties
+ };
+ }
+
+ return null;
+ },
+ dump: function(syntaxAsAst, pretty) {
+ return {
+ generic: this.generic,
+ types: dumpMapSyntax(this.types, !pretty, syntaxAsAst),
+ properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst),
+ atrules: dumpAtruleMapSyntax(this.atrules, !pretty, syntaxAsAst)
+ };
+ },
+ toString: function() {
+ return JSON.stringify(this.dump());
+ }
+};
+
+module.exports = Lexer;
+
+
+/***/ }),
+/* 23 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+const createCustomError = __webpack_require__(17);
+const generate = __webpack_require__(24);
+const defaultLoc = { offset: 0, line: 1, column: 1 };
+
+function locateMismatch(matchResult, node) {
+ const tokens = matchResult.tokens;
+ const longestMatch = matchResult.longestMatch;
+ const mismatchNode = longestMatch < tokens.length ? tokens[longestMatch].node || null : null;
+ const badNode = mismatchNode !== node ? mismatchNode : null;
+ let mismatchOffset = 0;
+ let mismatchLength = 0;
+ let entries = 0;
+ let css = '';
+ let start;
+ let end;
+
+ for (let i = 0; i < tokens.length; i++) {
+ const token = tokens[i].value;
+
+ if (i === longestMatch) {
+ mismatchLength = token.length;
+ mismatchOffset = css.length;
+ }
+
+ if (badNode !== null && tokens[i].node === badNode) {
+ if (i <= longestMatch) {
+ entries++;
+ } else {
+ entries = 0;
+ }
+ }
+
+ css += token;
+ }
+
+ if (longestMatch === tokens.length || entries > 1) { // last
+ start = fromLoc(badNode || node, 'end') || buildLoc(defaultLoc, css);
+ end = buildLoc(start);
+ } else {
+ start = fromLoc(badNode, 'start') ||
+ buildLoc(fromLoc(node, 'start') || defaultLoc, css.slice(0, mismatchOffset));
+ end = fromLoc(badNode, 'end') ||
+ buildLoc(start, css.substr(mismatchOffset, mismatchLength));
+ }
+
+ return {
+ css,
+ mismatchOffset,
+ mismatchLength,
+ start,
+ end
+ };
+}
+
+function fromLoc(node, point) {
+ const value = node && node.loc && node.loc[point];
+
+ if (value) {
+ return 'line' in value ? buildLoc(value) : value;
+ }
+
+ return null;
+}
+
+function buildLoc({ offset, line, column }, extra) {
+ const loc = {
+ offset,
+ line,
+ column
+ };
+
+ if (extra) {
+ const lines = extra.split(/\n|\r\n?|\f/);
+
+ loc.offset += extra.length;
+ loc.line += lines.length - 1;
+ loc.column = lines.length === 1 ? loc.column + extra.length : lines.pop().length + 1;
+ }
+
+ return loc;
+}
+
+const SyntaxReferenceError = function(type, referenceName) {
+ const error = createCustomError(
+ 'SyntaxReferenceError',
+ type + (referenceName ? ' `' + referenceName + '`' : '')
+ );
+
+ error.reference = referenceName;
+
+ return error;
+};
+
+const SyntaxMatchError = function(message, syntax, node, matchResult) {
+ const error = createCustomError('SyntaxMatchError', message);
+ const {
+ css,
+ mismatchOffset,
+ mismatchLength,
+ start,
+ end
+ } = locateMismatch(matchResult, node);
+
+ error.rawMessage = message;
+ error.syntax = syntax ? generate(syntax) : '';
+ error.css = css;
+ error.mismatchOffset = mismatchOffset;
+ error.mismatchLength = mismatchLength;
+ error.message = message + '\n' +
+ ' syntax: ' + error.syntax + '\n' +
+ ' value: ' + (css || '') + '\n' +
+ ' --------' + new Array(error.mismatchOffset + 1).join('-') + '^';
+
+ Object.assign(error, start);
+ error.loc = {
+ source: (node && node.loc && node.loc.source) || '',
+ start,
+ end
+ };
+
+ return error;
+};
+
+module.exports = {
+ SyntaxReferenceError,
+ SyntaxMatchError
+};
+
+
+/***/ }),
+/* 24 */
+/***/ ((module) => {
+
+function noop(value) {
+ return value;
+}
+
+function generateMultiplier(multiplier) {
+ if (multiplier.min === 0 && multiplier.max === 0) {
+ return '*';
+ }
+
+ if (multiplier.min === 0 && multiplier.max === 1) {
+ return '?';
+ }
+
+ if (multiplier.min === 1 && multiplier.max === 0) {
+ return multiplier.comma ? '#' : '+';
+ }
+
+ if (multiplier.min === 1 && multiplier.max === 1) {
+ return '';
+ }
+
+ return (
+ (multiplier.comma ? '#' : '') +
+ (multiplier.min === multiplier.max
+ ? '{' + multiplier.min + '}'
+ : '{' + multiplier.min + ',' + (multiplier.max !== 0 ? multiplier.max : '') + '}'
+ )
+ );
+}
+
+function generateTypeOpts(node) {
+ switch (node.type) {
+ case 'Range':
+ return (
+ ' [' +
+ (node.min === null ? '-∞' : node.min) +
+ ',' +
+ (node.max === null ? '∞' : node.max) +
+ ']'
+ );
+
+ default:
+ throw new Error('Unknown node type `' + node.type + '`');
+ }
+}
+
+function generateSequence(node, decorate, forceBraces, compact) {
+ var combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' ';
+ var result = node.terms.map(function(term) {
+ return generate(term, decorate, forceBraces, compact);
+ }).join(combinator);
+
+ if (node.explicit || forceBraces) {
+ result = (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]');
+ }
+
+ return result;
+}
+
+function generate(node, decorate, forceBraces, compact) {
+ var result;
+
+ switch (node.type) {
+ case 'Group':
+ result =
+ generateSequence(node, decorate, forceBraces, compact) +
+ (node.disallowEmpty ? '!' : '');
+ break;
+
+ case 'Multiplier':
+ // return since node is a composition
+ return (
+ generate(node.term, decorate, forceBraces, compact) +
+ decorate(generateMultiplier(node), node)
+ );
+
+ case 'Type':
+ result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>';
+ break;
+
+ case 'Property':
+ result = '<\'' + node.name + '\'>';
+ break;
+
+ case 'Keyword':
+ result = node.name;
+ break;
+
+ case 'AtKeyword':
+ result = '@' + node.name;
+ break;
+
+ case 'Function':
+ result = node.name + '(';
+ break;
+
+ case 'String':
+ case 'Token':
+ result = node.value;
+ break;
+
+ case 'Comma':
+ result = ',';
+ break;
+
+ default:
+ throw new Error('Unknown node type `' + node.type + '`');
+ }
+
+ return decorate(result, node);
+}
+
+module.exports = function(node, options) {
+ var decorate = noop;
+ var forceBraces = false;
+ var compact = false;
+
+ if (typeof options === 'function') {
+ decorate = options;
+ } else if (options) {
+ forceBraces = Boolean(options.forceBraces);
+ compact = Boolean(options.compact);
+ if (typeof options.decorate === 'function') {
+ decorate = options.decorate;
+ }
+ }
+
+ return generate(node, decorate, forceBraces, compact);
+};
+
+
+/***/ }),
+/* 25 */
+/***/ ((module) => {
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var keywords = Object.create(null);
+var properties = Object.create(null);
+var HYPHENMINUS = 45; // '-'.charCodeAt()
+
+function isCustomProperty(str, offset) {
+ offset = offset || 0;
+
+ return str.length - offset >= 2 &&
+ str.charCodeAt(offset) === HYPHENMINUS &&
+ str.charCodeAt(offset + 1) === HYPHENMINUS;
+}
+
+function getVendorPrefix(str, offset) {
+ offset = offset || 0;
+
+ // verdor prefix should be at least 3 chars length
+ if (str.length - offset >= 3) {
+ // vendor prefix starts with hyper minus following non-hyper minus
+ if (str.charCodeAt(offset) === HYPHENMINUS &&
+ str.charCodeAt(offset + 1) !== HYPHENMINUS) {
+ // vendor prefix should contain a hyper minus at the ending
+ var secondDashIndex = str.indexOf('-', offset + 2);
+
+ if (secondDashIndex !== -1) {
+ return str.substring(offset, secondDashIndex + 1);
+ }
+ }
+ }
+
+ return '';
+}
+
+function getKeywordDescriptor(keyword) {
+ if (hasOwnProperty.call(keywords, keyword)) {
+ return keywords[keyword];
+ }
+
+ var name = keyword.toLowerCase();
+
+ if (hasOwnProperty.call(keywords, name)) {
+ return keywords[keyword] = keywords[name];
+ }
+
+ var custom = isCustomProperty(name, 0);
+ var vendor = !custom ? getVendorPrefix(name, 0) : '';
+
+ return keywords[keyword] = Object.freeze({
+ basename: name.substr(vendor.length),
+ name: name,
+ vendor: vendor,
+ prefix: vendor,
+ custom: custom
+ });
+}
+
+function getPropertyDescriptor(property) {
+ if (hasOwnProperty.call(properties, property)) {
+ return properties[property];
+ }
+
+ var name = property;
+ var hack = property[0];
+
+ if (hack === '/') {
+ hack = property[1] === '/' ? '//' : '/';
+ } else if (hack !== '_' &&
+ hack !== '*' &&
+ hack !== '$' &&
+ hack !== '#' &&
+ hack !== '+' &&
+ hack !== '&') {
+ hack = '';
+ }
+
+ var custom = isCustomProperty(name, hack.length);
+
+ // re-use result when possible (the same as for lower case)
+ if (!custom) {
+ name = name.toLowerCase();
+ if (hasOwnProperty.call(properties, name)) {
+ return properties[property] = properties[name];
+ }
+ }
+
+ var vendor = !custom ? getVendorPrefix(name, hack.length) : '';
+ var prefix = name.substr(0, hack.length + vendor.length);
+
+ return properties[property] = Object.freeze({
+ basename: name.substr(prefix.length),
+ name: name.substr(hack.length),
+ hack: hack,
+ vendor: vendor,
+ prefix: prefix,
+ custom: custom
+ });
+}
+
+module.exports = {
+ keyword: getKeywordDescriptor,
+ property: getPropertyDescriptor,
+ isCustomProperty: isCustomProperty,
+ vendorPrefix: getVendorPrefix
+};
+
+
+/***/ }),
+/* 26 */
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+var tokenizer = __webpack_require__(27);
+var isIdentifierStart = tokenizer.isIdentifierStart;
+var isHexDigit = tokenizer.isHexDigit;
+var isDigit = tokenizer.isDigit;
+var cmpStr = tokenizer.cmpStr;
+var consumeNumber = tokenizer.consumeNumber;
+var TYPE = tokenizer.TYPE;
+var anPlusB = __webpack_require__(29);
+var urange = __webpack_require__(30);
+
+var cssWideKeywords = ['unset', 'initial', 'inherit'];
+var calcFunctionNames = ['calc(', '-moz-calc(', '-webkit-calc('];
+
+// https://www.w3.org/TR/css-values-3/#lengths
+var LENGTH = {
+ // absolute length units
+ 'px': true,
+ 'mm': true,
+ 'cm': true,
+ 'in': true,
+ 'pt': true,
+ 'pc': true,
+ 'q': true,
+
+ // relative length units
+ 'em': true,
+ 'ex': true,
+ 'ch': true,
+ 'rem': true,
+
+ // viewport-percentage lengths
+ 'vh': true,
+ 'vw': true,
+ 'vmin': true,
+ 'vmax': true,
+ 'vm': true
+};
+
+var ANGLE = {
+ 'deg': true,
+ 'grad': true,
+ 'rad': true,
+ 'turn': true
+};
+
+var TIME = {
+ 's': true,
+ 'ms': true
+};
+
+var FREQUENCY = {
+ 'hz': true,
+ 'khz': true
+};
+
+// https://www.w3.org/TR/css-values-3/#resolution (https://drafts.csswg.org/css-values/#resolution)
+var RESOLUTION = {
+ 'dpi': true,
+ 'dpcm': true,
+ 'dppx': true,
+ 'x': true // https://github.com/w3c/csswg-drafts/issues/461
+};
+
+// https://drafts.csswg.org/css-grid/#fr-unit
+var FLEX = {
+ 'fr': true
+};
+
+// https://www.w3.org/TR/css3-speech/#mixing-props-voice-volume
+var DECIBEL = {
+ 'db': true
+};
+
+// https://www.w3.org/TR/css3-speech/#voice-props-voice-pitch
+var SEMITONES = {
+ 'st': true
+};
+
+// safe char code getter
+function charCode(str, index) {
+ return index < str.length ? str.charCodeAt(index) : 0;
+}
+
+function eqStr(actual, expected) {
+ return cmpStr(actual, 0, actual.length, expected);
+}
+
+function eqStrAny(actual, expected) {
+ for (var i = 0; i < expected.length; i++) {
+ if (eqStr(actual, expected[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// IE postfix hack, i.e. 123\0 or 123px\9
+function isPostfixIeHack(str, offset) {
+ if (offset !== str.length - 2) {
+ return false;
+ }
+
+ return (
+ str.charCodeAt(offset) === 0x005C && // U+005C REVERSE SOLIDUS (\)
+ isDigit(str.charCodeAt(offset + 1))
+ );
+}
+
+function outOfRange(opts, value, numEnd) {
+ if (opts && opts.type === 'Range') {
+ var num = Number(
+ numEnd !== undefined && numEnd !== value.length
+ ? value.substr(0, numEnd)
+ : value
+ );
+
+ if (isNaN(num)) {
+ return true;
+ }
+
+ if (opts.min !== null && num < opts.min) {
+ return true;
+ }
+
+ if (opts.max !== null && num > opts.max) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function consumeFunction(token, getNextToken) {
+ var startIdx = token.index;
+ var length = 0;
+
+ // balanced token consuming
+ do {
+ length++;
+
+ if (token.balance <= startIdx) {
+ break;
+ }
+ } while (token = getNextToken(length));
+
+ return length;
+}
+
+// TODO: implement
+// can be used wherever , , ,