Added polyfill

This commit is contained in:
Sebastian Seedorf
2020-11-15 00:18:23 +01:00
parent 6189b95b6e
commit fc47d6ec26
11 changed files with 887 additions and 34 deletions

808
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
"install-prod-win": "npm install --only=dev && npm install --only=prod && npm run build && rmdir /Q/S node_modules && npm install --only=prod"
},
"dependencies": {
"@10xjs/polyfill-analyzer": "^0.1.0",
"connect-redis": "^5.0.0",
"cookie-parser": "~1.4.4",
"env-var": "^6.3.0",
@@ -25,9 +26,12 @@
"json-prune": "^1.1.0",
"node-fetch": "^2.6.1",
"node-sass-middleware": "0.11.0",
"polyfill-library": "^3.97.0",
"proper-url-join": "^2.1.1",
"pug": "^3.0.0",
"redis": "^3.0.2",
"threads": "^1.6.3",
"tiny-worker": "^2.3.0",
"uuid": "^8.3.1",
"winston": "^3.3.3"
},

View File

@@ -10,7 +10,7 @@
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"strict": true,
"sourceRoot": "./src",
"sourceRoot": "../js-source",
"rootDir": ".",
"skipLibCheck": true,
"baseUrl": "."

View File

@@ -6,7 +6,7 @@ import * as redisStore from 'connect-redis';
import * as session from 'express-session';
import * as sassMiddleware from 'node-sass-middleware';
import indexRouter from './routes';
import {HttpLogger, Redis, Config, setupAuthProxy, getReloadRouter} from './utils';
import {HttpLogger, Redis, Config, setupAuthProxy, getReloadRouter, polyfillRoute} from './utils';
import {Store} from 'express-session';
export const app = express();
@@ -46,6 +46,7 @@ router.use(session({
}));
// static config
router.use("/js/polyfill.js", polyfillRoute);
router.use(sassMiddleware({
src: path.join(__dirname, '../public'),
dest: path.join(__dirname, '../public'),

View File

@@ -58,7 +58,7 @@ export class Resolvable<T, U extends Array<unknown>> extends FetchOnce<T, U> {
}
}
export class WaitForSync<T, U extends Array<unknown>> extends FetchOnce<T, U> {
export class WaitForSync<T> extends FetchOnce<T, never> {
protected state: ResolvableState = ResolvableState.PENDING;
constructor() {

View File

@@ -4,3 +4,4 @@ export {Logger, HttpLogger} from './logging';
export {setupAuthProxy} from './auth-proxy';
export {Resolvable, WaitForSync} from './helpers/resolvable';
export {getReloadRouter} from './auto-reload';
export {polyfillRoute} from './polyfill';

View File

@@ -17,7 +17,7 @@ const logger = winston.createLogger({
});
const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"] as const;
type LogLevels = typeof levels[number];
type LogLevels = typeof levels[number]|"log";
const wrapper = (original: LeveledLogMethod) => {
return (...args: unknown[]) => {
return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" "));
@@ -30,6 +30,7 @@ export const Logger = {
for (const level of levels) {
Logger[level] = wrapper(logger[level]);
}
Logger.log = wrapper(logger["silly"]);
export const HttpLogger: RequestHandler = (req, res, next) => {
const start = Date.now();

View File

@@ -0,0 +1,25 @@
// workers/add.js
import {expose} from "threads/worker";
import * as fs from "fs";
import {analyze} from '@10xjs/polyfill-analyzer';
import allPolyfills from '@10xjs/polyfill-analyzer/dist/polyfills';
import {PolyfillFeatureList} from "polyfill-library";
expose(() => {
const exclude = [
"console.markTimeline",
"console.timeline",
"console.timelineEnd",
];
const featureList = analyze({
source: fs.readFileSync('./public/js/bundle.js', 'utf-8'),
include: allPolyfills.filter(x => !exclude.includes(x)),
// Not all features listed by polyfillLibrary.listAllPolyfills()` can be detected.
unsupportedPolyfill: 'ignore',
});
const feats: PolyfillFeatureList = {};
for (const feature of featureList) {
feats[feature] = {};
}
return feats;
});

35
src/utils/polyfill.ts Normal file
View File

@@ -0,0 +1,35 @@
import {RequestHandler} from 'express';
import * as polyfillLibrary from 'polyfill-library';
import {Config} from './config';
import {PolyfillFeatureList} from 'polyfill-library';
import {Logger} from './logging';
import {WaitForSync} from './helpers/resolvable';
import {spawn, Thread, Worker} from 'threads';
import {WorkerFunction} from 'threads/dist/types/worker';
const features = new WaitForSync<PolyfillFeatureList>();
(async () => {
const worker = await spawn<WorkerFunction>(new Worker("./polyfill-worker"));
const feats = await worker() as PolyfillFeatureList;
await Thread.terminate(worker);
return feats;
})()
.then(feats => {
Logger.debug("Polyfill analysed:", feats);
features.setData(feats);
})
.catch(err => features.setError(err));
export const polyfillRoute: RequestHandler = async (req, res) => {
const polyfillBundle = await polyfillLibrary.getPolyfillString({
uaString: req.header("user-agent"),
features: await features.resolve(),
minify: Config.isProduction,
unknown: "polyfill",
stream: false,
});
res.setHeader('Content-Type', 'text/javascript');
res.send(polyfillBundle);
};

37
src/utils/types/polyfill.d.ts vendored Normal file
View File

@@ -0,0 +1,37 @@
declare module "polyfill-library" {
import {Readable} from 'stream';
function listAllPolyfills(): string[];
function describePolyfill(featureName: string): Promise<PolyfillMetadata|undefined>;
function getOptions(opts: Partial<PolyfillOptions>): PolyfillOptions;
function getPolyfills(opts: Partial<PolyfillOptions>): Promise<PolyfillFeatures>;
function getPolyfillString(opts: Partial<PolyfillOptions>&{stream?: true}): Readable;
function getPolyfillString(opts: Partial<PolyfillOptions>&{stream: false}): Promise<string>;
type PolyfillMetadata = {
};
type PolyfillFeatureList = {
[featureName: string]: {
flags?: string[]
}
};
type PolyfillOptions = {
minify: boolean,
unknown: 'polyfill'|'ignore',
features: PolyfillFeatureList,
excludes: string[],
uaString: string,
rum: boolean,
};
type PolyfillFeature = {
flags: string[],
dependencyOf: string[],
aliasOf: string[],
}
type PolyfillFeatures = { [featureName: string]: PolyfillFeature };
}

View File

@@ -8,6 +8,7 @@ html
script(type='text/javascript', async, src=baseUrl+'/auto-reload/client.js')
body
block content
script(type='text/javascript', src=baseUrl+'/js/polyfill.js')
script(type='text/javascript', src=baseUrl+'/js/require-2.3.6.min.js')
script(type='text/javascript', src=baseUrl+'/js/bundle.js')
script.