Allow excluding wordlists when building for browserify

This commit is contained in:
junderw 2019-03-26 11:58:52 +09:00
parent 6dedef08d4
commit c30b47b63d
No known key found for this signature in database
GPG Key ID: B256185D3A971908
9 changed files with 202 additions and 61 deletions

View File

@ -16,6 +16,62 @@ When a checksum is invalid, warn the user that the phrase is not something gener
However, there should be other checks in place, such as checking to make sure the user is inputting 12 words or more separated by a space. ie. `phrase.trim().split(/\s+/g).length >= 12`
## Removing wordlists from webpack/browserify
Browserify/Webpack bundles can get very large if you include all the wordlists, so you can now exclude wordlists to make your bundle lighter.
For example, if we want to exclude all wordlists besides chinese_simplified, you could build using the browserify command below.
```bash
$ browserify -r bip39 -s bip39 \
--exclude=./wordlists/english.json \
--exclude=./wordlists/japanese.json \
--exclude=./wordlists/spanish.json \
--exclude=./wordlists/italian.json \
--exclude=./wordlists/french.json \
--exclude=./wordlists/korean.json \
--exclude=./wordlists/chinese_traditional.json \
> bip39.browser.js
```
This will create a bundle that only contains the chinese_simplified wordlist, and it will be the default wordlist for all calls without explicit wordlists.
This is how it will look in the browser console.
```javascript
> bip39.entropyToMnemonic('00000000000000000000000000000000')
"的 的 的 的 的 的 的 的 的 的 的 在"
> bip39.wordlists.chinese_simplified
Array(2048) [ "的", "一", "是", "在", "不", "了", "有", "和", "人", "这", … ]
> bip39.wordlists.english
undefined
> bip39.wordlists.japanese
undefined
> bip39.wordlists.spanish
undefined
> bip39.wordlists.italian
undefined
> bip39.wordlists.french
undefined
> bip39.wordlists.korean
undefined
> bip39.wordlists.chinese_traditional
undefined
```
For a list of supported wordlists check the wordlists folder. The name of the json file (minus the extension) is the name of the key to access the wordlist.
You can also change the default wordlist at runtime if you dislike the wordlist you were given as default.
```javascript
> bip39.entropyToMnemonic('00000000000000000000000000000fff')
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あまい ろんり"
> bip39.setDefaultWordlist('italian')
undefined
> bip39.entropyToMnemonic('00000000000000000000000000000fff')
"abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco aforisma zibetto"
```
## Installation
``` bash
npm install bip39

51
src/_wordlists.js Normal file
View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// browserify by default only pulls in files that are hard coded in requires
// In order of last to first in this file, the default wordlist will be chosen
// based on what is present. (Bundles may remove wordlists they don't need)
const wordlists = {};
exports.wordlists = wordlists;
let _default;
exports._default = _default;
try {
exports._default = _default = require('./wordlists/chinese_simplified.json');
wordlists.chinese_simplified = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/chinese_traditional.json');
wordlists.chinese_traditional = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/korean.json');
wordlists.korean = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/french.json');
wordlists.french = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/italian.json');
wordlists.italian = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/spanish.json');
wordlists.spanish = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/japanese.json');
wordlists.japanese = _default;
wordlists.JA = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/english.json');
wordlists.english = _default;
wordlists.EN = _default;
}
catch (err) { }

View File

@ -5,18 +5,13 @@ const pbkdf2_1 = require("pbkdf2");
const randomBytes = require("randombytes");
// use unorm until String.prototype.normalize gets better browser support
const unorm = require("unorm");
const CHINESE_SIMPLIFIED_WORDLIST = require("./wordlists/chinese_simplified.json");
const CHINESE_TRADITIONAL_WORDLIST = require("./wordlists/chinese_traditional.json");
const ENGLISH_WORDLIST = require("./wordlists/english.json");
const FRENCH_WORDLIST = require("./wordlists/french.json");
const ITALIAN_WORDLIST = require("./wordlists/italian.json");
const JAPANESE_WORDLIST = require("./wordlists/japanese.json");
const KOREAN_WORDLIST = require("./wordlists/korean.json");
const SPANISH_WORDLIST = require("./wordlists/spanish.json");
const DEFAULT_WORDLIST = ENGLISH_WORDLIST;
const _wordlists_1 = require("./_wordlists");
let DEFAULT_WORDLIST = _wordlists_1._default;
const INVALID_MNEMONIC = 'Invalid mnemonic';
const INVALID_ENTROPY = 'Invalid entropy';
const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
const WORDLIST_REQUIRED = 'A wordlist is required but a default could not be found.\n' +
'Please explicitly pass a 2048 word array explicitly.';
function lpad(str, padString, length) {
while (str.length < length)
str = padString + str;
@ -74,6 +69,9 @@ async function mnemonicToSeedHexAsync(mnemonic, password) {
exports.mnemonicToSeedHexAsync = mnemonicToSeedHexAsync;
function mnemonicToEntropy(mnemonic, wordlist) {
wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
const words = unorm.nfkd(mnemonic).split(' ');
if (words.length % 3 !== 0)
throw new Error(INVALID_MNEMONIC);
@ -109,6 +107,9 @@ function entropyToMnemonic(entropy, wordlist) {
if (!Buffer.isBuffer(entropy))
entropy = Buffer.from(entropy, 'hex');
wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
// 128 <= ENT <= 256
if (entropy.length < 16)
throw new TypeError(INVALID_ENTROPY);
@ -124,7 +125,7 @@ function entropyToMnemonic(entropy, wordlist) {
const index = binaryToByte(binary);
return wordlist[index];
});
return wordlist === JAPANESE_WORDLIST
return wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093' // Japanese wordlist
? words.join('\u3000')
: words.join(' ');
}
@ -147,15 +148,11 @@ function validateMnemonic(mnemonic, wordlist) {
return true;
}
exports.validateMnemonic = validateMnemonic;
exports.wordlists = {
EN: ENGLISH_WORDLIST,
JA: JAPANESE_WORDLIST,
chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST,
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST,
english: ENGLISH_WORDLIST,
french: FRENCH_WORDLIST,
italian: ITALIAN_WORDLIST,
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST,
};
function setDefaultWordlist(language) {
const result = _wordlists_1.wordlists[language];
if (result)
DEFAULT_WORDLIST = result;
}
exports.setDefaultWordlist = setDefaultWordlist;
var _wordlists_2 = require("./_wordlists");
exports.wordlists = _wordlists_2.wordlists;

42
ts_src/_wordlists.ts Normal file
View File

@ -0,0 +1,42 @@
// browserify by default only pulls in files that are hard coded in requires
// In order of last to first in this file, the default wordlist will be chosen
// based on what is present. (Bundles may remove wordlists they don't need)
const wordlists: { [index: string]: string[] } = {};
let _default: string[] | undefined;
try {
_default = require('./wordlists/chinese_simplified.json');
wordlists.chinese_simplified = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/chinese_traditional.json');
wordlists.chinese_traditional = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/korean.json');
wordlists.korean = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/french.json');
wordlists.french = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/italian.json');
wordlists.italian = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/spanish.json');
wordlists.spanish = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/japanese.json');
wordlists.japanese = _default as string[];
wordlists.JA = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/english.json');
wordlists.english = _default as string[];
wordlists.EN = _default as string[];
} catch (err) {}
// Last one to overwrite wordlist gets to be default.
export { wordlists, _default };

View File

@ -1,23 +1,18 @@
import createHash = require('create-hash');
import * as createHash from 'create-hash';
import { pbkdf2 as pbkdf2Async, pbkdf2Sync as pbkdf2 } from 'pbkdf2';
import randomBytes = require('randombytes');
import * as randomBytes from 'randombytes';
// use unorm until String.prototype.normalize gets better browser support
import unorm = require('unorm');
import * as unorm from 'unorm';
import { _default as _DEFAULT_WORDLIST, wordlists } from './_wordlists';
import CHINESE_SIMPLIFIED_WORDLIST = require('./wordlists/chinese_simplified.json');
import CHINESE_TRADITIONAL_WORDLIST = require('./wordlists/chinese_traditional.json');
import ENGLISH_WORDLIST = require('./wordlists/english.json');
import FRENCH_WORDLIST = require('./wordlists/french.json');
import ITALIAN_WORDLIST = require('./wordlists/italian.json');
import JAPANESE_WORDLIST = require('./wordlists/japanese.json');
import KOREAN_WORDLIST = require('./wordlists/korean.json');
import SPANISH_WORDLIST = require('./wordlists/spanish.json');
const DEFAULT_WORDLIST = ENGLISH_WORDLIST;
let DEFAULT_WORDLIST: string[] | undefined = _DEFAULT_WORDLIST;
const INVALID_MNEMONIC = 'Invalid mnemonic';
const INVALID_ENTROPY = 'Invalid entropy';
const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
const WORDLIST_REQUIRED =
'A wordlist is required but a default could not be found.\n' +
'Please explicitly pass a 2048 word array explicitly.';
function lpad(str: string, padString: string, length: number): string {
while (str.length < length) str = padString + str;
@ -97,6 +92,9 @@ export function mnemonicToEntropy(
wordlist?: string[],
): string {
wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
const words = unorm.nfkd(mnemonic).split(' ');
if (words.length % 3 !== 0) throw new Error(INVALID_MNEMONIC);
@ -135,6 +133,9 @@ export function entropyToMnemonic(
): string {
if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex');
wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
// 128 <= ENT <= 256
if (entropy.length < 16) throw new TypeError(INVALID_ENTROPY);
@ -151,7 +152,7 @@ export function entropyToMnemonic(
return wordlist![index];
});
return wordlist === JAPANESE_WORDLIST
return wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093' // Japanese wordlist
? words.join('\u3000')
: words.join(' ');
}
@ -181,16 +182,9 @@ export function validateMnemonic(
return true;
}
export const wordlists = {
EN: ENGLISH_WORDLIST,
JA: JAPANESE_WORDLIST,
export function setDefaultWordlist(language: string): void {
const result = wordlists[language];
if (result) DEFAULT_WORDLIST = result;
}
chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST,
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST,
english: ENGLISH_WORDLIST,
french: FRENCH_WORDLIST,
italian: ITALIAN_WORDLIST,
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST,
};
export { wordlists } from './_wordlists';

View File

@ -24,7 +24,8 @@
"resolveJsonModule": true
},
"include": [
"ts_src/**/*.ts"
"ts_src/**/*.ts",
"ts_src/**/*.json"
],
"exclude": [
"**/*.spec.ts",

5
types/_wordlists.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare const wordlists: {
[index: string]: string[];
};
declare let _default: string[] | undefined;
export { wordlists, _default };

14
types/index.d.ts vendored
View File

@ -7,15 +7,5 @@ export declare function mnemonicToEntropy(mnemonic: string, wordlist?: string[])
export declare function entropyToMnemonic(entropy: Buffer | string, wordlist?: string[]): string;
export declare function generateMnemonic(strength?: number, rng?: (size: number) => Buffer, wordlist?: string[]): string;
export declare function validateMnemonic(mnemonic: string, wordlist?: string[]): boolean;
export declare const wordlists: {
EN: string[];
JA: string[];
chinese_simplified: string[];
chinese_traditional: string[];
english: string[];
french: string[];
italian: string[];
japanese: string[];
korean: string[];
spanish: string[];
};
export declare function setDefaultWordlist(language: string): void;
export { wordlists } from './_wordlists';

5
types/wordlists.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare const wordlists: {
[index: string]: string[];
};
declare let _default: string[] | undefined;
export { wordlists, _default };