Added frontend tests
This commit is contained in:
2436
package-lock.json
generated
2436
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -8,11 +8,14 @@
|
|||||||
"debug-client": "tsc-watch --project ./public/js-source",
|
"debug-client": "tsc-watch --project ./public/js-source",
|
||||||
"debug-server": "tsc-watch --project . --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"",
|
"debug-server": "tsc-watch --project . --onSuccess \"node --enable-source-maps --use-openssl-ca --unhandled-rejections=strict ./out/index\"",
|
||||||
"debug": "concurrently npm:debug-*",
|
"debug": "concurrently npm:debug-*",
|
||||||
|
"test": "nyc mocha -r ts-node/register ./public/js-source/test/**/*.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index",
|
"production": "node --use-openssl-ca --unhandled-rejections=strict ./out/index",
|
||||||
"install-debug": "npm install && npm run build",
|
"install-debug": "npm install && npm run build",
|
||||||
"install-prod": "npm install --only=dev && npm install --only=prod && npm run build && rm -r node_modules && npm install --only=prod",
|
"install-prod": "npm install --only=dev && npm install --only=prod && npm run build && npm run del-node-module && npm install --only=prod",
|
||||||
"install-prod-win": "npm install --only=dev && npm install --only=prod && npm run build && rmdir /Q/S node_modules && npm install --only=prod"
|
"del-node-module:default": "rm -r node_modules",
|
||||||
|
"del-node-module:windows": "if exist node_modules rmdir /Q/S node_modules",
|
||||||
|
"del-node-module": "run-script-os"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
@@ -25,18 +28,27 @@
|
|||||||
"role-acl": "^4.5.4"
|
"role-acl": "^4.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chai": "^4.2.14",
|
||||||
"@types/compression": "^1.7.0",
|
"@types/compression": "^1.7.0",
|
||||||
"@types/express": "^4.17.8",
|
"@types/express": "^4.17.8",
|
||||||
"@types/http-errors": "^1.8.0",
|
"@types/http-errors": "^1.8.0",
|
||||||
|
"@types/mocha": "^8.0.4",
|
||||||
"@types/node": "^14.14.7",
|
"@types/node": "^14.14.7",
|
||||||
"@types/node-sass-middleware": "0.0.31",
|
"@types/node-sass-middleware": "0.0.31",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.7.0",
|
"@typescript-eslint/eslint-plugin": "^4.7.0",
|
||||||
"@typescript-eslint/parser": "^4.7.0",
|
"@typescript-eslint/parser": "^4.7.0",
|
||||||
|
"chai": "^4.2.0",
|
||||||
"concurrently": "^5.3.0",
|
"concurrently": "^5.3.0",
|
||||||
"eslint": "^7.13.0",
|
"eslint": "^7.13.0",
|
||||||
"eslint-plugin-no-null": "^1.0.2",
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"fetch-mock": "^9.10.7",
|
||||||
|
"jsdom": "^16.4.0",
|
||||||
|
"mocha": "^8.2.1",
|
||||||
"node-watch": "^0.7.0",
|
"node-watch": "^0.7.0",
|
||||||
|
"nyc": "^15.1.0",
|
||||||
|
"rewire": "^5.0.0",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
"tsc-watch": "^4.2.9",
|
"tsc-watch": "^4.2.9",
|
||||||
"typescript": "^4.0.5"
|
"typescript": "^4.0.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ export {setConfig} from './utils/utils';
|
|||||||
|
|
||||||
import {getUserName} from './SomeModule';
|
import {getUserName} from './SomeModule';
|
||||||
|
|
||||||
getUserName().then((name) => {
|
function updateUserName(name: string): void {
|
||||||
const node = document.createElement('span');
|
const node = document.createElement('span');
|
||||||
node.innerText = `This user name is fetched with Javascript: ${name}`;
|
node.innerText = `This user name is fetched with Javascript: ${name}`;
|
||||||
document.getElementsByTagName("body")[0].appendChild(node);
|
document.getElementsByTagName("body")[0].appendChild(node);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
getUserName().then(updateUserName);
|
||||||
@@ -5,21 +5,27 @@ enum ResolvableState {
|
|||||||
DONE
|
DONE
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchOnce<T> {
|
class FetchOnce<T, U extends Array<unknown>> {
|
||||||
protected data: T|undefined;
|
protected data: T|undefined;
|
||||||
protected error: unknown|undefined;
|
protected error: unknown|undefined;
|
||||||
protected state: ResolvableState = ResolvableState.WAITING;
|
protected state: ResolvableState = ResolvableState.WAITING;
|
||||||
protected pendings: [(res: Promise<T>|T) => void, (reason: unknown) => void][] = [];
|
protected pendings: [(res: Promise<T>|T) => void, (reason: unknown) => void][] = [];
|
||||||
|
|
||||||
constructor(protected fetchMethod?: () => Promise<T>) { }
|
constructor(protected fetchMethod?: (...args: U) => Promise<T>) { }
|
||||||
|
|
||||||
public resolve(): Promise<T> {
|
public reset(): void {
|
||||||
|
this.data = undefined;
|
||||||
|
this.error = undefined;
|
||||||
|
this.state = ResolvableState.WAITING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolve(...args: U): Promise<T> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case ResolvableState.WAITING:
|
case ResolvableState.WAITING:
|
||||||
this.state = ResolvableState.PENDING;
|
this.state = ResolvableState.PENDING;
|
||||||
this.pendings.push([resolve, reject]);
|
this.pendings.push([resolve, reject]);
|
||||||
if (this.fetchMethod) this.parsePromise(this.fetchMethod());
|
if (this.fetchMethod) this.parsePromise(this.fetchMethod(...args));
|
||||||
break;
|
break;
|
||||||
case ResolvableState.PENDING:
|
case ResolvableState.PENDING:
|
||||||
this.pendings.push([resolve, reject]);
|
this.pendings.push([resolve, reject]);
|
||||||
@@ -51,13 +57,13 @@ class FetchOnce<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Resolvable<T> extends FetchOnce<T> {
|
export class Resolvable<T, U extends Array<unknown>> extends FetchOnce<T, U> {
|
||||||
constructor(fetchMethod: () => Promise<T>) {
|
constructor(fetchMethod: (...args: U) => Promise<T>) {
|
||||||
super(fetchMethod);
|
super(fetchMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WaitForSync<T> extends FetchOnce<T> {
|
export class WaitForSync<T> extends FetchOnce<T, never> {
|
||||||
protected state: ResolvableState = ResolvableState.PENDING;
|
protected state: ResolvableState = ResolvableState.PENDING;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -75,4 +81,9 @@ export class WaitForSync<T> extends FetchOnce<T> {
|
|||||||
this.parsePromise((async () => { throw error; })());
|
this.parsePromise((async () => { throw error; })());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
super.reset();
|
||||||
|
this.state = ResolvableState.PENDING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,23 @@ export function getConfig(): Promise<ClientConfig> {
|
|||||||
return configWaiter.resolve();
|
return configWaiter.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserInfo(): Promise<UserInfo|undefined> {
|
export function resetConfig(): void {
|
||||||
|
return configWaiter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserInfo(): Promise<Partial<UserInfo>|undefined> {
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
const res = await fetch(config.EXTERNAL_BASE_URL + "/api/user");
|
const res = await fetch(config.EXTERNAL_BASE_URL + "/api/user");
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserInfo = {
|
||||||
|
email: string,
|
||||||
|
email_verified: boolean,
|
||||||
|
family_name: string,
|
||||||
|
given_name: string,
|
||||||
|
groups: string[],
|
||||||
|
name: string,
|
||||||
|
preferred_username: string,
|
||||||
|
sub: string,
|
||||||
|
};
|
||||||
57
public/js-source/test/SomeModule.ts
Normal file
57
public/js-source/test/SomeModule.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import {describe, it} from 'mocha';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import * as fetchMock from 'fetch-mock';
|
||||||
|
import {getUserName} from '../src/SomeModule';
|
||||||
|
import {setConfig, UserInfo} from '../src/utils/utils';
|
||||||
|
|
||||||
|
describe('frontend:SomeModule', () => {
|
||||||
|
const CONFIG = {EXTERNAL_BASE_URL: "http://demo.url"};
|
||||||
|
before(() => {
|
||||||
|
setConfig(CONFIG);
|
||||||
|
fetchMock.config.overwriteRoutes = true;
|
||||||
|
});
|
||||||
|
it('should return username', async () => {
|
||||||
|
const tests: [Partial<UserInfo>, string][] = [
|
||||||
|
[
|
||||||
|
{name: "John Doe"},
|
||||||
|
"John Doe",
|
||||||
|
], [
|
||||||
|
{name: "John Doe", email: "some.mail@example.com"},
|
||||||
|
"John Doe",
|
||||||
|
], [
|
||||||
|
{name: "", email: "some.mail@example.com"},
|
||||||
|
"",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
for (const [USER_INFO, RESULT] of tests) {
|
||||||
|
fetchMock.mock('http://demo.url/api/user', {
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(USER_INFO),
|
||||||
|
});
|
||||||
|
const name = await getUserName();
|
||||||
|
expect(name).to.deep.equal(RESULT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should return default string', async () => {
|
||||||
|
const RESULT = "No name found!";
|
||||||
|
const tests: (Partial<UserInfo>|unknown)[] = [
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
{name: null},
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
null,
|
||||||
|
{email: "some.mail@example.com"},
|
||||||
|
{name: undefined},
|
||||||
|
{},
|
||||||
|
];
|
||||||
|
for (const USER_INFO of tests) {
|
||||||
|
fetchMock.mock('http://demo.url/api/user', {
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(USER_INFO),
|
||||||
|
});
|
||||||
|
const name = await getUserName();
|
||||||
|
expect(name).to.deep.equal(RESULT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
43
public/js-source/test/index.ts
Normal file
43
public/js-source/test/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {describe, it} from 'mocha';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import {JSDOM} from 'jsdom';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import * as rewire from 'rewire';
|
||||||
|
|
||||||
|
describe("frontend:index", () => {
|
||||||
|
const updateUserName = rewire("../src/index").__get__("updateUserName") as (name: string) => void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const dom = new JSDOM(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
{url: 'http://localhost'},
|
||||||
|
);
|
||||||
|
|
||||||
|
// noinspection JSConstantReassignment
|
||||||
|
global.window = dom.window;
|
||||||
|
// noinspection JSConstantReassignment
|
||||||
|
global.document = dom.window.document;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateUserName", (done) => {
|
||||||
|
const NAME = "Patrick Star";
|
||||||
|
const RESULT = "This user name is fetched with Javascript: Patrick Star";
|
||||||
|
updateUserName(NAME);
|
||||||
|
// give the browser a chance to update the DOM
|
||||||
|
setTimeout(() => {
|
||||||
|
const span = document.getElementsByTagName("span");
|
||||||
|
expect(span).to.not.be.null;
|
||||||
|
expect(span.length).to.be.greaterThan(0);
|
||||||
|
expect(span[0].innerText).to.deep.equal(RESULT);
|
||||||
|
done();
|
||||||
|
}, 5);
|
||||||
|
});
|
||||||
|
});
|
||||||
123
public/js-source/test/utils.ts
Normal file
123
public/js-source/test/utils.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import {setConfig, getConfig, resetConfig, getUserInfo, UserInfo} from '../src/utils/utils';
|
||||||
|
import {Resolvable, WaitForSync} from '../src/utils/resolvable';
|
||||||
|
import {describe, it} from 'mocha';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import * as fetchMock from 'fetch-mock';
|
||||||
|
|
||||||
|
describe('frontend:utils - setConfig/getConfig', () => {
|
||||||
|
const CONFIG = {EXTERNAL_BASE_URL: "http://demo.url"};
|
||||||
|
afterEach(() => resetConfig());
|
||||||
|
|
||||||
|
it('should return config (afterwards)', async () => {
|
||||||
|
setConfig(CONFIG);
|
||||||
|
const config = await getConfig();
|
||||||
|
expect(config).to.deep.equal(CONFIG);
|
||||||
|
});
|
||||||
|
it('should return config (beforehand)', (done) => {
|
||||||
|
getConfig().then((config) => {
|
||||||
|
expect(config).to.deep.equal(CONFIG);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
setConfig(CONFIG);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('frontend:utils - getUserInfo', () => {
|
||||||
|
const CONFIG = {EXTERNAL_BASE_URL: "http://demo.url"};
|
||||||
|
const USER_INFO: Partial<UserInfo> = {
|
||||||
|
name: "John Doe",
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => setConfig(CONFIG));
|
||||||
|
afterEach(() => resetConfig());
|
||||||
|
|
||||||
|
it('should equal fetched', async () => {
|
||||||
|
setConfig(CONFIG);
|
||||||
|
fetchMock.mock('http://demo.url/api/user', {
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(USER_INFO),
|
||||||
|
});
|
||||||
|
const userInfo = await getUserInfo();
|
||||||
|
expect(userInfo).to.deep.equal(USER_INFO);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('frontend:utils - resolvable', () => {
|
||||||
|
const DATA = 5;
|
||||||
|
const ERROR = new Error("Custom error!");
|
||||||
|
|
||||||
|
it('waitForSync should return data (afterwards)', async () => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.setData(DATA);
|
||||||
|
const data = await resolvable.resolve();
|
||||||
|
expect(data).to.deep.equal(DATA);
|
||||||
|
});
|
||||||
|
it('waitForSync should return data (beforehand)', (done) => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.resolve().then((data) => {
|
||||||
|
expect(data).to.deep.equal(DATA);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
resolvable.setData(DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('waitForSync should error (afterwards)', async () => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.setError(ERROR);
|
||||||
|
try {
|
||||||
|
await resolvable.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.deep.equal(ERROR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('waitForSync should error (beforehand)', (done) => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.resolve().catch((err) => {
|
||||||
|
expect(err).to.deep.equal(ERROR);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
resolvable.setError(ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('waitForSync should resolve data twice', async () => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.setData(DATA);
|
||||||
|
const data1 = await resolvable.resolve();
|
||||||
|
const data2 = await resolvable.resolve();
|
||||||
|
expect(data1).to.deep.equal(DATA);
|
||||||
|
expect(data2).to.deep.equal(DATA);
|
||||||
|
});
|
||||||
|
it('waitForSync should error twice', async () => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
resolvable.setError(ERROR);
|
||||||
|
try {
|
||||||
|
await resolvable.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.deep.equal(ERROR);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await resolvable.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.deep.equal(ERROR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('waitForSync should wait for resolution twice', (done) => {
|
||||||
|
const resolvable = new WaitForSync<number>();
|
||||||
|
Promise.all([
|
||||||
|
resolvable.resolve().then((data) => {
|
||||||
|
expect(data).to.deep.equal(DATA);
|
||||||
|
}), resolvable.resolve().then((data) => {
|
||||||
|
expect(data).to.deep.equal(DATA);
|
||||||
|
}),
|
||||||
|
]).then(() => done());
|
||||||
|
resolvable.setData(DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolvable should resolve', async () => {
|
||||||
|
const resolvable = new Resolvable(async () => DATA);
|
||||||
|
const data = await resolvable.resolve();
|
||||||
|
expect(data).to.deep.equal(DATA);
|
||||||
|
});
|
||||||
|
});
|
||||||
10
public/js-source/utils/types/userinfo.d.ts
vendored
10
public/js-source/utils/types/userinfo.d.ts
vendored
@@ -1,10 +0,0 @@
|
|||||||
type UserInfo = {
|
|
||||||
email: string,
|
|
||||||
email_verified: boolean,
|
|
||||||
family_name: string,
|
|
||||||
given_name: string,
|
|
||||||
groups: string[],
|
|
||||||
name: string,
|
|
||||||
preferred_username: string,
|
|
||||||
sub: string,
|
|
||||||
};
|
|
||||||
2
src/types/extend-request.d.ts
vendored
2
src/types/extend-request.d.ts
vendored
@@ -14,7 +14,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
getUserInfo(): Promise<UserInfo|undefined>;
|
getUserInfo(): Promise<Partial<UserInfo>|undefined>;
|
||||||
noLogging: boolean|undefined;
|
noLogging: boolean|undefined;
|
||||||
permissionDetails?: import('role-acl').Permission;
|
permissionDetails?: import('role-acl').Permission;
|
||||||
}
|
}
|
||||||
|
|||||||
9
types/auth-proxy.d.ts
vendored
9
types/auth-proxy.d.ts
vendored
@@ -1,9 +0,0 @@
|
|||||||
declare namespace Express {
|
|
||||||
interface Request {
|
|
||||||
getUserInfo(): Promise<import('pkg-express-utils').UserInfo|undefined>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Response {
|
|
||||||
initLogout(): boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
types/json-prune.d.ts
vendored
5
types/json-prune.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
declare module "json-prune" {
|
|
||||||
function prune(object: unknown): string;
|
|
||||||
|
|
||||||
export = prune;
|
|
||||||
}
|
|
||||||
5
types/logging.d.ts
vendored
5
types/logging.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
declare namespace Express {
|
|
||||||
interface Request {
|
|
||||||
noLogging: boolean|undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
types/permissions.d.ts
vendored
5
types/permissions.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
declare namespace Express {
|
|
||||||
interface Request {
|
|
||||||
permissionDetails?: import('role-acl').Permission;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
types/polyfill.d.ts
vendored
37
types/polyfill.d.ts
vendored
@@ -1,37 +0,0 @@
|
|||||||
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