Improve tests (#18)

Replaced mocha with jest. 
Test are split into unit and integration categories.
Dropped ESM dependency. Code is now being compiled into CJS
This commit is contained in:
Pavel
2021-02-14 00:35:02 +03:00
committed by GitHub
parent 399166efe8
commit 416221ea17
14 changed files with 4692 additions and 5542 deletions

View File

@ -32,7 +32,7 @@ jobs:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
- run: npm run test:unit
env:
CI: true

4
jest.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

9684
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,16 +5,18 @@
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
"test": "mocha --timeout 10000 -r esm -r ts-node/register src/**/*.spec.ts",
"build": "tsc && rsync -r src/internal/aqua/*.js dist/internal/aqua",
"test": "jest --watch",
"test:all": "jest",
"test:unit": "jest --testPathPattern=src/__test__/unit",
"test:integration": "jest --testPathPattern=src/__test__/integration",
"build": "tsc",
"build:webpack": "webpack --mode production"
},
"repository": "https://github.com/fluencelabs/fluence-js",
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {
"@fluencelabs/aquamarine-stepper": "0.4.2",
"@fluencelabs/fluence-network-environment": "1.0.8",
"@fluencelabs/aquamarine-stepper": "0.4.4",
"async": "3.2.0",
"base64-js": "1.3.1",
"bs58": "4.0.1",
@ -30,25 +32,22 @@
"uuid": "8.3.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7",
"@types/base64-js": "1.2.5",
"@types/bs58": "4.0.1",
"@types/chai": "4.2.11",
"@types/mocha": "7.0.2",
"@types/jest": "^26.0.20",
"@types/uuid": "8.3.0",
"assert": "2.0.0",
"chai": "4.2.0",
"clean-webpack-plugin": "3.0.0",
"babel-jest": "^26.6.3",
"babel-plugin-replace-ts-export-assignment": "0.0.2",
"esm": "^3.2.25",
"html-webpack-plugin": "^3.2.0",
"jest": "^26.6.3",
"libp2p-ts": "https://github.com/ChainSafe/libp2p-ts.git#fca072c9764436ef71f974a211bb1befae432575",
"mocha": "^8.2.1",
"mocha-loader": "^5.1.5",
"text-encoding": "^0.7.0",
"ts-jest": "^26.5.0",
"ts-loader": "7.0.5",
"ts-mocha": "8.0.0",
"typescript": "^3.9.5",
"webpack": "4.43.0",
"webpack-cli": "3.3.11",
"webpack-dev-server": "3.11.0"
"typescript": "^3.9.5"
}
}

View File

@ -0,0 +1,135 @@
import {
addBlueprint,
addProvider,
addScript,
createService,
getBlueprints,
getInterfaces,
getModules,
getProviders,
removeScript,
uploadModule,
} from '../../internal/builtins';
import { ModuleConfig } from '../../internal/moduleConfig';
import { createConnectedClient } from '../util';
const dev2multiaddr = '/dns4/dev.fluence.dev/tcp/19003/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb';
const dev3multiaddr = '/dns4/dev.fluence.dev/tcp/19004/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB';
const dev2peerId = '12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9';
describe('Builtins usage suite', () => {
it('get_modules', async function () {
const client = await createConnectedClient(dev2multiaddr);
let modulesList = await getModules(client);
expect(modulesList).not.toBeUndefined;
});
it('get_interfaces', async function () {
const client = await createConnectedClient(dev2multiaddr);
let interfaces = await getInterfaces(client);
expect(interfaces).not.toBeUndefined;
});
it('get_blueprints', async function () {
const client = await createConnectedClient(dev2multiaddr);
let bpList = await getBlueprints(client);
expect(bpList).not.toBeUndefined;
});
it('upload_modules', async function () {
const client = await createConnectedClient(dev2multiaddr);
console.log('peerid: ' + client.selfPeerId);
let config: ModuleConfig = {
name: 'test_broken_module',
mem_pages_count: 100,
logger_enabled: true,
wasi: {
envs: { a: 'b' },
preopened_files: ['a', 'b'],
mapped_dirs: { c: 'd' },
},
mounted_binaries: { e: 'f' },
};
let base64 = 'MjNy';
await uploadModule(client, 'test_broken_module', base64, config);
});
it('add_blueprint', async function () {
const client = await createConnectedClient(dev2multiaddr);
let bpId = 'some';
let bpIdReturned = await addBlueprint(client, 'test_broken_blueprint', ['test_broken_module'], bpId);
expect(bpIdReturned).toEqual(bpId);
});
// FIXME:: there is no error on broken blueprint from a node
it.skip('create_service', async function () {
const client = await createConnectedClient(dev2multiaddr);
let serviceId = await createService(client, 'test_broken_blueprint');
// TODO there is no error on broken blueprint from a node
expect(serviceId).not.toBeUndefined;
});
it('add_provider', async function () {
const client = await createConnectedClient(dev2multiaddr);
let key = Math.random().toString(36).substring(7);
let buf = Buffer.from(key);
let r = Math.random().toString(36).substring(7);
await addProvider(client, buf, dev2peerId, r);
let pr = await getProviders(client, buf);
console.log(pr);
console.log(r);
expect(r).toEqual(pr[0][0].service_id);
});
it('add and remove script', async function () {
const client = await createConnectedClient(dev3multiaddr);
console.log('peerid: ' + client.selfPeerId);
let script = `
(seq
(call "${client.relayPeerId}" ("op" "identity") [])
(call "${client.selfPeerId}" ("test" "test1") ["1" "2" "3"] result)
)
`;
let resMakingPromise = new Promise((resolve) => {
client.registerCallback('test', 'test1', (args, _) => {
resolve([...args]);
return {};
});
});
let scriptId = await addScript(client, script);
await resMakingPromise
.then((args) => {
console.log('final!');
expect(args as string[]).toEqual(['1', '2', '3']);
})
.finally(() => {
removeScript(client, scriptId);
});
expect(scriptId).not.toBeUndefined;
});
});

View File

@ -1,27 +1,13 @@
import { expect } from 'chai';
import 'mocha';
import { encode } from 'bs58';
import { certificateFromString, certificateToString, issue } from '../internal/trust/certificate';
import { TrustGraph } from '../internal/trust/trust_graph';
import { nodeRootCert } from '../internal/trust/misc';
import { generatePeerId, peerIdToSeed, seedToPeerId } from '../internal/peerIdUtils';
import { FluenceClientImpl } from '../internal/FluenceClientImpl';
import { createConnectedClient } from './util';
import { certificateFromString, certificateToString, issue } from '../../internal/trust/certificate';
import { TrustGraph } from '../../internal/trust/trust_graph';
import { nodeRootCert } from '../../internal/trust/misc';
import { generatePeerId, peerIdToSeed, seedToPeerId } from '../../internal/peerIdUtils';
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
import { createConnectedClient, createLocalClient } from '../util';
import log from 'loglevel';
import { createClient } from '../api';
import { createClient } from '../../api';
import Multiaddr from 'multiaddr';
import {
addBlueprint, addProvider,
addScript,
createService,
getBlueprints, getInterfaces,
getModules, getProviders,
removeScript,
uploadModule
} from '../internal/builtins';
import {dev} from "@fluencelabs/fluence-network-environment";
import {ModuleConfig, Wasi} from "../internal/moduleConfig";
const devNodeAddress = '/dns4/dev.fluence.dev/tcp/19001/wss/p2p/12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9';
const devNodePeerId = '12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9';
@ -33,7 +19,7 @@ describe('Typescript usage suite', () => {
let seedStr = encode(seed);
log.trace('SEED STR: ' + seedStr);
let pid = await seedToPeerId(seedStr);
expect(peerIdToSeed(pid)).to.be.equal(seedStr);
expect(peerIdToSeed(pid)).toEqual(seedStr);
});
it('should serialize and deserialize certificate correctly', async function () {
@ -51,18 +37,14 @@ describe('Typescript usage suite', () => {
let deser = await certificateFromString(cert);
let ser = certificateToString(deser);
expect(ser).to.be.equal(cert);
expect(ser).toEqual(cert);
});
// delete `.skip` and run `npm run test` to check service's and certificate's api with Fluence nodes
it.skip('should perform tests on certs', async function () {
this.timeout(15000);
await testCerts();
});
describe.skip('should make connection to network', async function () {
this.timeout(30000);
describe('should make connection to network', function () {
const testProcedure = async (client: FluenceClientImpl) => {
let resMakingPromise = new Promise((resolve) => {
client.registerCallback('test', 'test', (args, _) => {
@ -96,7 +78,7 @@ describe('Typescript usage suite', () => {
// assert
const res = await testProcedure(client);
expect(res).to.deep.equal(['world']);
expect(res).toEqual(['world']);
});
it('address as multiaddr', async function () {
@ -108,7 +90,7 @@ describe('Typescript usage suite', () => {
// assert
const res = await testProcedure(client);
expect(res).to.deep.equal(['world']);
expect(res).toEqual(['world']);
});
it('address as node', async function () {
@ -123,7 +105,7 @@ describe('Typescript usage suite', () => {
// assert
const res = await testProcedure(client);
expect(res).to.deep.equal(['world']);
expect(res).toEqual(['world']);
});
it('peerid as peer id', async function () {
@ -136,7 +118,7 @@ describe('Typescript usage suite', () => {
// assert
const res = await testProcedure(client);
expect(res).to.deep.equal(['world']);
expect(res).toEqual(['world']);
});
it('peerid as seed', async function () {
@ -149,14 +131,13 @@ describe('Typescript usage suite', () => {
// assert
const res = await testProcedure(client);
expect(res).to.deep.equal(['world']);
expect(res).toEqual(['world']);
});
});
it.skip('should make a call through the network', async function () {
this.timeout(30000);
it('should make a call through the network', async function () {
// arrange
const client = await createConnectedClient(dev[0].multiaddr);
const client = await createConnectedClient(devNodeAddress);
client.registerCallback('test', 'test', (args, _) => {
log.trace('should make a call through the network, called "test" "test" with args', args);
@ -191,11 +172,10 @@ describe('Typescript usage suite', () => {
// assert
const res = await resMakingPromise;
expect(res).to.deep.equal(['some d', 'some c', 'some b', 'some a']);
expect(res).toEqual(['some d', 'some c', 'some b', 'some a']);
});
it.skip('fireAndForget should work', async function () {
this.timeout(30000);
it('fireAndForget should work', async function () {
// arrange
const client = await createConnectedClient(devNodeAddress);
@ -221,130 +201,10 @@ describe('Typescript usage suite', () => {
// assert
const res = await resMakingPromise;
expect(res).to.deep.equal(['some d', 'some c', 'some b', 'some a']);
expect(res).toEqual(['some d', 'some c', 'some b', 'some a']);
});
it.skip('get_modules', async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let modulesList = await getModules(client);
expect(modulesList).not.to.be.undefined;
});
it.skip('get_interfaces', async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let interfaces = await getInterfaces(client);
expect(interfaces).not.to.be.undefined;
});
it.skip('get_blueprints', async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let bpList = await getBlueprints(client);
expect(bpList).not.to.be.undefined;
});
it("upload_modules", async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
console.log("peerid: " + client.selfPeerId)
let config: ModuleConfig = {
name: "test_broken_module",
mem_pages_count: 100,
logger_enabled: true,
wasi: {
envs: {a: "b"},
preopened_files: ["a", "b"],
mapped_dirs: {c: "d"},
},
mounted_binaries: {e: "f"}
}
let base64 = "MjNy"
await uploadModule(client, "test_broken_module", base64, config);
});
it.skip("add_blueprint", async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let bpId = "some"
let bpIdReturned = await addBlueprint(client, "test_broken_blueprint", ["test_broken_module"], bpId);
expect(bpIdReturned).to.be.equal(bpId);
});
it.skip("create_service", async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let serviceId = await createService(client, "test_broken_blueprint");
// TODO there is no error on broken blueprint from a node
expect(serviceId).not.to.be.undefined;
});
it.skip("add_provider", async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[2].multiaddr);
let key = Math.random().toString(36).substring(7);
let buf = Buffer.from(key)
let r = Math.random().toString(36).substring(7);
await addProvider(client, buf, dev[2].peerId, r);
let pr = await getProviders(client, buf);
console.log(pr)
console.log(r)
expect(r).to.be.equal(pr[0][0].service_id);
});
it.skip('add and remove script', async function () {
this.timeout(30000);
const client = await createConnectedClient(dev[3].multiaddr);
console.log("peerid: " + client.selfPeerId)
let script = `
(seq
(call "${client.relayPeerId}" ("op" "identity") [])
(call "${client.selfPeerId}" ("test" "test1") ["1" "2" "3"] result)
)
`;
let resMakingPromise = new Promise((resolve) => {
client.registerCallback('test', 'test1', (args, _) => {
resolve([...args]);
return {};
});
});
let scriptId = await addScript(client, script);
await resMakingPromise.then((args) => {
console.log("final!")
expect(args as string[]).to.be.deep.equal(["1", "2", "3"]);
}).finally(() => {
removeScript(client, scriptId);
})
expect(scriptId).not.to.be.undefined;
});
it.skip('fetch should work', async function () {
this.timeout(30000);
it('fetch should work', async function () {
// arrange
const client = await createConnectedClient(devNodeAddress);
@ -358,10 +218,10 @@ describe('Typescript usage suite', () => {
const [res] = await client.fetch(script, ['result'], data);
// assert
expect(res.external_addresses).to.be.not.undefined;
expect(res.external_addresses).not.toBeUndefined;
});
it.skip('two clients should work inside the same time browser', async function () {
it('two clients should work inside the same time browser', async function () {
// arrange
const pid1 = await generatePeerId();
const client1 = new FluenceClientImpl(pid1);
@ -394,10 +254,10 @@ describe('Typescript usage suite', () => {
await client1.sendScript(script, data);
let res = await resMakingPromise;
expect(res).to.deep.equal(['some a', 'some b', 'some c', 'some d']);
expect(res).toEqual(['some a', 'some b', 'some c', 'some d']);
});
it.skip('event registration should work', async function () {
it('event registration should work', async function () {
// arrange
const pid1 = await generatePeerId();
const client1 = new FluenceClientImpl(pid1);
@ -424,7 +284,7 @@ describe('Typescript usage suite', () => {
// assert
let res = await resMakingPromise;
expect(res).to.deep.equal({
expect(res).toEqual({
type: 'test',
args: ['world'],
});
@ -461,10 +321,10 @@ export async function testCerts() {
let certs = await trustGraph2.getCertificates(key2.toB58String());
// root certificate could be different because nodes save trusts with bigger `expiresAt` date and less `issuedAt` date
expect(certs[0].chain[1].issuedFor.toB58String()).to.be.equal(extended.chain[1].issuedFor.toB58String());
expect(certs[0].chain[1].signature).to.be.equal(extended.chain[1].signature);
expect(certs[0].chain[1].expiresAt).to.be.equal(extended.chain[1].expiresAt);
expect(certs[0].chain[1].issuedAt).to.be.equal(extended.chain[1].issuedAt);
expect(certs[0].chain[1].issuedFor.toB58String()).toEqual(extended.chain[1].issuedFor.toB58String());
expect(certs[0].chain[1].signature).toEqual(extended.chain[1].signature);
expect(certs[0].chain[1].expiresAt).toEqual(extended.chain[1].expiresAt);
expect(certs[0].chain[1].issuedAt).toEqual(extended.chain[1].issuedAt);
await cl1.disconnect();
await cl2.disconnect();

View File

@ -1,5 +0,0 @@
--require ts-node/register
--require @babel/register
src/__test__/**/*.spec.ts

View File

@ -1,6 +1,4 @@
import 'mocha';
import { expect } from 'chai';
import { createLocalClient } from './util';
import { createLocalClient } from '../util';
describe('== AIR suite', () => {
it('check init_peer_id', async function () {
@ -21,7 +19,7 @@ describe('== AIR suite', () => {
await client.sendScript(script);
// assert
expect(res).to.be.equal(client.selfPeerId);
expect(res).toEqual(client.selfPeerId);
});
it('call local function', async function () {
@ -43,7 +41,7 @@ describe('== AIR suite', () => {
await client.sendScript(script);
// assert
expect(res).to.be.equal(arg);
expect(res).toEqual(arg);
});
it('check particle arguments', async function () {
@ -66,7 +64,7 @@ describe('== AIR suite', () => {
await client.sendScript(script, data);
// assert
expect(res).to.be.equal('hello');
expect(res).toEqual('hello');
});
it('check security tetraplet', async function () {
@ -102,7 +100,7 @@ describe('== AIR suite', () => {
// assert
const tetraplet = res.tetraplets[0][0];
expect(tetraplet).to.contain({
expect(tetraplet).toMatchObject({
service_id: 'make_data_service',
function_name: 'make_data',
json_path: '$.field',
@ -110,7 +108,6 @@ describe('== AIR suite', () => {
});
it('check chain of services work properly', async function () {
this.timeout(5000);
// arrange
const client = await createLocalClient();
@ -151,8 +148,8 @@ describe('== AIR suite', () => {
await client.sendScript(script);
// assert
expect(res1).to.be.equal(arg1);
expect(res2).to.be.equal(arg2);
expect(res3).to.be.deep.equal([res1, res2]);
expect(res1).toEqual(arg1);
expect(res2).toEqual(arg2);
expect(res3).toEqual([res1, res2]);
});
});

View File

@ -1,6 +1,4 @@
import { expect } from 'chai';
import 'mocha';
import { parseAIR } from '../internal/stepper';
import { parseAIR } from '../../internal/stepper';
describe('== AST parsing suite', () => {
it('parse simple script and return ast', async function () {
@ -10,7 +8,7 @@ describe('== AST parsing suite', () => {
ast = JSON.parse(ast);
expect(ast).to.deep.equal({
expect(ast).toEqual({
Call: {
peer_part: { PeerPk: { Variable: 'node' } },
function_part: { ServiceIdWithFuncName: [{ Literal: 'service' }, { Literal: 'function' }] },

View File

@ -1,18 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param wasm
* @param {string} init_user_id
* @param {string} aqua
* @param {string} prev_data
* @param {string} data
* @param {string} log_level
* @returns {string}
*/
export function invoke(wasm: any, init_user_id: string, aqua: string, prev_data: Uint8Array, data: Uint8Array, log_level: string): string;
export function ast(wasm: any, script: string): string;
export function return_current_peer_id(wasm: any, peerId: string, arg0: any): void;
export function return_call_service_result(wasm: any, ret: string, arg0: any): void;
export function getStringFromWasm0(wasm: any, arg1: any, arg2: any): string
export function free(wasm: any, ptr: any, len: any): void

View File

@ -1 +0,0 @@
export * from "./index_bg.js";

View File

@ -18,11 +18,12 @@ export function getUint8Memory0(wasm) {
return cachegetUint8Memory0;
}
const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder;
const lTextEncoder = typeof TextEncoder === 'undefined' ? module.require('util').TextEncoder : TextEncoder;
let cachedTextEncoder = new lTextEncoder('utf-8');
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
const encodeString =
typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
@ -31,15 +32,17 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
view.set(buf);
return {
read: arg.length,
written: buf.length
written: buf.length,
};
};
});
export function passStringToWasm0(wasm, arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length);
getUint8Memory0(wasm).subarray(ptr, ptr + buf.length).set(buf);
getUint8Memory0(wasm)
.subarray(ptr, ptr + buf.length)
.set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
@ -53,22 +56,20 @@ export function passStringToWasm0(wasm, arg, malloc, realloc) {
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
if (code > 0x7f) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3);
ptr = realloc(ptr, len, (len = offset + arg.length * 3));
const view = getUint8Memory0(wasm).subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
}
WASM_VECTOR_LEN = offset;
return ptr;
@ -82,7 +83,7 @@ export function getInt32Memory0(wasm) {
return cachegetInt32Memory0;
}
const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
const lTextDecoder = typeof TextDecoder === 'undefined' ? module.require('util').TextDecoder : TextDecoder;
let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
@ -154,7 +155,6 @@ export function return_call_service_result(wasm, ret, arg0) {
var len1 = WASM_VECTOR_LEN;
getInt32Memory0(wasm)[arg0 / 4 + 1] = len1;
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr1;
}
export function free(wasm, ptr, len) {

View File

@ -21,10 +21,7 @@ import { ParticleHandler, CallServiceResult, SecurityTetraplet } from './commonT
import PeerId from 'peer-id';
import log from 'loglevel';
import { wasmBs64 } from '@fluencelabs/aquamarine-stepper';
import Instance = WebAssembly.Instance;
import Exports = WebAssembly.Exports;
import ExportValue = WebAssembly.ExportValue;
import wasmBs64 from '@fluencelabs/aquamarine-stepper';
export type InterpreterInvoke = (
init_user_id: string,
@ -47,6 +44,10 @@ type LogImport = {
log_utf8_string: (level: any, target: any, offset: any, size: any) => void;
};
type Exports = any;
type Instance = any;
type ExportValue = any;
class HostImportsConfig {
exports: Exports | undefined;
newImportObject: () => ImportObject;

View File

@ -1,58 +0,0 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const production = (process.env.NODE_ENV === 'production');
const config = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.spec\.ts$/,
use: 'mocha-loader',
exclude: /node_modules/,
},
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'bundle'),
},
node: {
fs: 'empty'
},
plugins: [
new HtmlWebpackPlugin(),
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
]
};
if (production) {
config.mode = 'production';
} else {
config.mode = 'development';
config.devtool = 'inline-source-map';
config.devServer = {
contentBase: './bundle',
hot: false
};
config.plugins = [
...config.plugins,
new webpack.HotModuleReplacementPlugin()
];
}
module.exports = config;