Added polyfill
This commit is contained in:
@@ -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'),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
|
||||
25
src/utils/polyfill-worker.ts
Normal file
25
src/utils/polyfill-worker.ts
Normal 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
35
src/utils/polyfill.ts
Normal 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
37
src/utils/types/polyfill.d.ts
vendored
Normal 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user