├── .gitignore ├── .npmignore ├── tests ├── packed.txt ├── cli.test.js ├── unpacked.txt ├── main.test.js ├── plurals.test.js └── verbs.test.js ├── bin ├── unpack.js └── pack.js ├── src ├── key-value.js ├── index.js ├── packPlurals.js └── verbs.js ├── .eslintrc ├── scripts └── build.js ├── package.json ├── scratch.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | scripts 2 | test 3 | .esformatter 4 | .eslintrc 5 | .babelrc 6 | .gitignore 7 | TODO.MD 8 | viz 9 | changelog.md 10 | scratch.js 11 | -------------------------------------------------------------------------------- /tests/packed.txt: -------------------------------------------------------------------------------- 1 | {"name":"fun","words":"Dinosaur¦brontosaurus,trex","tags":{"Dinosaur":{"isA":"Animal"},"Animal":{"isA":"Noun"}},"regex":{"Dinosaur":[".osaurus$"],"Exaggeration":["uuu+"]},"plurals":"horse|s,fl|y|ies,jeep|s,one|"} 2 | -------------------------------------------------------------------------------- /bin/unpack.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var unpack = require('../src').unpack 3 | var fs = require('fs'); 4 | 5 | var file = process.argv[2] 6 | if (!file) { 7 | console.warn('pass in a file to read as a second param') 8 | } 9 | 10 | fs.readFile(file, (err, r) => { 11 | var str = r.toString() 12 | console.log(unpack(str)) 13 | }) 14 | -------------------------------------------------------------------------------- /bin/pack.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var pack = require('../src').pack; 3 | var fs = require('fs'); 4 | 5 | var file = process.argv[2] 6 | if (!file) { 7 | console.warn('pass in a file to read as a second param') 8 | } 9 | 10 | fs.readFile(file, (err, r) => { 11 | var str = r.toString() 12 | console.log(pack(JSON.parse(str))) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/cli.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const exec = require('shelljs').exec 3 | 4 | test('test-pack-cli', function(t) { 5 | const cmd = 'node ./bin/pack.js ./tests/unpacked.txt ' 6 | exec(cmd) 7 | t.ok(true, 'runs pack cli') 8 | t.end() 9 | }) 10 | 11 | test('test-unpack-cli', function(t) { 12 | const cmd = 'node ./bin/unpack.js ./tests/packed.txt ' 13 | exec(cmd) 14 | t.ok(true, 'runs unpack cli') 15 | t.end() 16 | }) 17 | -------------------------------------------------------------------------------- /tests/unpacked.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fun", 3 | "words": { 4 | "brontosaurus": "Dinosaur", 5 | "trex": "Dinosaur" 6 | }, 7 | "tags": { 8 | "Dinosaur": { 9 | "isA": "Animal" 10 | }, 11 | "Animal": { 12 | "isA": "Noun" 13 | } 14 | }, 15 | "regex": { 16 | ".osaurus$": "Dinosaur", 17 | "uuu+": "Exaggeration" 18 | }, 19 | "plurals": { 20 | "horse": "horses", 21 | "fly": "flies", 22 | "jeep": "jeeps", 23 | "one": "one" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/key-value.js: -------------------------------------------------------------------------------- 1 | //pivot category-like data (key-vals with frequently shared values) 2 | // into a 'value:[key, key]' format 3 | const keyValue = function(obj) { 4 | obj = Object.keys(obj).reduce((h, k) => { 5 | if (typeof obj[k] === 'string') { 6 | h[obj[k]] = h[obj[k]] || [] 7 | h[obj[k]].push(k) 8 | } else { 9 | //handle values as an array 10 | obj[k].forEach(key => { 11 | h[key] = h[key] || [] 12 | h[key].push(k) 13 | }) 14 | } 15 | return h 16 | }, {}) 17 | return obj 18 | } 19 | module.exports = keyValue 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const nlp = require('compromise') 2 | const efrt = require('efrt') 3 | const keyValue = require('./key-value') 4 | const verbs = require('./verbs') 5 | const plurals = require('./packPlurals') 6 | 7 | const pack = function(obj) { 8 | obj = Object.assign({}, obj) 9 | if (obj.words) { 10 | obj.words = efrt.pack(obj.words) 11 | } 12 | if (obj.patterns) { 13 | obj.patterns = keyValue(obj.patterns) 14 | } 15 | if (obj.regex) { 16 | obj.regex = keyValue(obj.regex) 17 | } 18 | if (obj.plurals) { 19 | obj.plurals = plurals(obj.plurals) 20 | } 21 | if (obj.conjugations) { 22 | obj.conjugations = verbs(obj.conjugations) 23 | } 24 | return JSON.stringify(obj, null, 0) 25 | } 26 | 27 | module.exports = { 28 | pack: pack, 29 | unpack: nlp.unpack 30 | } 31 | -------------------------------------------------------------------------------- /tests/main.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const both = require('../src') 3 | const pack = both.pack 4 | const unpack = both.unpack 5 | 6 | const plugin = { 7 | name: 'fun', 8 | words: { 9 | brontosaurus: 'Dinosaur', 10 | trex: 'Dinosaur' 11 | }, 12 | tags: { 13 | Dinosaur: { 14 | isA: 'Animal' 15 | }, 16 | Animal: { 17 | isA: 'Noun' 18 | } 19 | }, 20 | regex: { 21 | '.osaurus$': 'Dinosaur', 22 | 'uuu+': 'Exaggeration' 23 | }, 24 | plurals: { 25 | horse: 'horses', 26 | fly: 'flies', 27 | jeep: 'jeeps', 28 | one: 'one' 29 | } 30 | } 31 | 32 | test('test-all', function(t) { 33 | const str = pack(plugin) 34 | const unpacked = unpack(str) 35 | t.deepEqual(plugin, unpacked, 'in==out') 36 | // t.ok(plugin.postProcess(true), 'postProcess runs'); 37 | t.end() 38 | }) 39 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true 4 | }, 5 | "parserOptions": { 6 | "ecmaVersion": 6, 7 | "sourceType": "module", 8 | "ecmaFeatures": { 9 | "jsx": false 10 | } 11 | }, 12 | "rules": { 13 | "no-cond-assign": 2, 14 | "no-var": 1, 15 | "prefer-const": 1, 16 | "no-extra-parens": 0, 17 | "no-dupe-keys": 2, 18 | "no-unreachable": 2, 19 | "eqeqeq": 1, 20 | "keyword-spacing": 0, 21 | "no-native-reassign": 2, 22 | "no-redeclare": 2, 23 | "radix": 1, 24 | "indent": 0, 25 | "quotes": [0, "single", "avoid-escape"], 26 | "no-shadow": 2, 27 | "no-unused-vars": 1, 28 | "no-lonely-if": 1, 29 | "no-use-before-define": 2, 30 | "no-bitwise": 2, 31 | "no-dupe-class-members": 2, 32 | "guard-for-in": 1, 33 | "consistent-return": 2, 34 | "no-octal-escape": 2, 35 | "no-constant-condition": 1, 36 | "no-unused-expressions": 2, 37 | "no-undefined": 0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const exec = require('shelljs').exec 3 | const chalk = require('chalk') 4 | const pkg = require('../package.json') 5 | 6 | const browserify = '"node_modules/.bin/browserify"' 7 | const derequire = '"node_modules/.bin/derequire"' 8 | const es5 = './builds/compromise-plugin.js' 9 | 10 | const fileSize = function(src) { 11 | const stats = fs.statSync(src) 12 | return (stats['size'] / 1000.0).toFixed(2) 13 | } 14 | 15 | //cleanup. remove old builds 16 | exec('rm -rf ./builds && mkdir builds') 17 | 18 | console.log(chalk.yellow(' 🕑 creating full build..')) 19 | 20 | const banner = 21 | '/* compromise-plugin v' + 22 | pkg.version + 23 | '\n github.com/nlp-compromise/compromise-plugin\n MIT\n*/\n' 24 | 25 | //es5 main (browserify + derequire) 26 | cmd = browserify + ' "./src/index.js" --standalone nlp-plugin' 27 | // cmd += ' -t uglifyify'; 28 | // cmd += ' -p bundle-collapser/plugin'; 29 | // cmd += ' --presets [ es2015 ] '; 30 | cmd += ' | ' + derequire 31 | cmd += ' >> ' + es5 32 | exec(cmd) 33 | 34 | console.log(chalk.green(' done full build! es5 ' + fileSize(es5) + 'k\n')) 35 | -------------------------------------------------------------------------------- /src/packPlurals.js: -------------------------------------------------------------------------------- 1 | //packs data in the form of {key:'value'}, where key + value have a shared prefix 2 | const normalize = function(str) { 3 | str = str.replace(/[,|]/g, '') 4 | return str 5 | } 6 | 7 | const compareBoth = function(a, b) { 8 | a = normalize(a).split('') 9 | b = normalize(b).split('') 10 | const common = [] 11 | let suffA = [] 12 | let suffB = [] 13 | let len = a.length 14 | if (b.length > a.length) { 15 | len = b.length 16 | } 17 | for (let i = 0; i < len; i++) { 18 | if (a[i] === b[i]) { 19 | common.push(a[i]) 20 | } else { 21 | suffA = a.slice(i, a.length) 22 | suffB = b.slice(i, b.length) 23 | break 24 | } 25 | } 26 | let str = common.join('') 27 | if (suffA.length > 0) { 28 | str += '|' + suffA.join('') 29 | } 30 | str += '|' + suffB.join('') 31 | return str 32 | } 33 | 34 | const packPlurals = function(obj) { 35 | return Object.keys(obj) 36 | .map(k => { 37 | return compareBoth(k, obj[k]) 38 | }) 39 | .join(',') 40 | } 41 | 42 | module.exports = packPlurals 43 | 44 | // var str = packPlurals({ 45 | // house: 'houses', 46 | // matrix: 'matrices' 47 | // }); 48 | // console.log(str); 49 | // var unpack = require('./unpack'); 50 | // console.log(unpack(str)); 51 | -------------------------------------------------------------------------------- /tests/plurals.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const both = require('../src') 3 | const pack = both.pack 4 | const unpack = both.unpack 5 | 6 | const plugin = { 7 | name: 'tryplurals', 8 | plurals: { 9 | addendum: 'addenda', 10 | barracks: 'barracks', 11 | beau: 'beaux', 12 | leaf: 'leaves', 13 | libretto: 'libretti', 14 | loaf: 'loaves', 15 | man: 'men', 16 | matrix: 'matrices', 17 | memorandum: 'memoranda', 18 | modulus: 'moduli', 19 | mosquito: 'mosquitoes', 20 | move: 'moves', 21 | opus: 'opera', 22 | ovum: 'ova', 23 | ox: 'oxen', 24 | tableau: 'tableaux', 25 | thief: 'thieves', 26 | tooth: 'teeth', 27 | tornado: 'tornados', 28 | tuxedo: 'tuxedos', 29 | zero: 'zeros' 30 | } 31 | } 32 | test('pack-plurals', function(t) { 33 | const str = pack(plugin) 34 | const unpacked = unpack(str) 35 | t.deepEqual(plugin, unpacked, 'in==out') 36 | t.end() 37 | }) 38 | 39 | test('packPlural-edge-cases', function(t) { 40 | const input = { 41 | name: 'tryplurals2', 42 | plurals: { 43 | aaaaaaa: 'aaaaaaa', //equal 44 | aaaaaaaa: 'bb', //shorter 45 | aa: 'bbbbbbb', //longer 46 | a: 'a and some b' //spaces 47 | } 48 | } 49 | const str = pack(input) 50 | const output = unpack(str) 51 | t.deepEqual(input, output, 'in==out') 52 | t.end() 53 | }) 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compromise-plugin", 3 | "description": "author and compress nlp-compromise plugins", 4 | "version": "0.0.9", 5 | "author": "Spencer Kelly (http://spencermounta.in)", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/nlp-compromise/compromise-plugin.git" 9 | }, 10 | "main": "./builds/compromise-plugin.js", 11 | "bin": { 12 | "compromise-pack": "./bin/pack.js", 13 | "compromise-unpack": "./bin/unpack.js" 14 | }, 15 | "scripts": { 16 | "test": "\"node_modules/.bin/tape\" \"./tests/*.test.js\" | \"node_modules/.bin/tap-spec\" --color", 17 | "build": "node ./scripts/build.js", 18 | "watch": "./node_modules/.bin/amble ./scratch.js " 19 | }, 20 | "prettier": { 21 | "trailingComma": "none", 22 | "tabWidth": 2, 23 | "semi": false, 24 | "singleQuote": true, 25 | "printWidth": 100 26 | }, 27 | "peerDependencies": { 28 | "compromise": "^11.0.0" 29 | }, 30 | "dependencies": { 31 | "efrt": "^2.0.3" 32 | }, 33 | "devDependencies": { 34 | "amble": "0.0.7", 35 | "babel-preset-es2015": "^6.24.1", 36 | "browserify": "16.2.3", 37 | "chalk": "^2.1.0", 38 | "derequire": "^2.0.6", 39 | "shelljs": "0.8.3", 40 | "tap-spec": "5.0.0", 41 | "tape": "^4.8.0", 42 | "uglify-js": "^3.1.4", 43 | "uglifyify": "5.0.1" 44 | }, 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /src/verbs.js: -------------------------------------------------------------------------------- 1 | //supported verb forms: 2 | const forms = ['PastTense', 'PresentTense', 'Gerund', 'Participle'] 3 | //find the shared substring between the two forms 4 | const substring = function(inf, str) { 5 | for (let k = 0; k < inf.length; k++) { 6 | if (str.length <= k || inf[k] !== str[k]) { 7 | // console.log(str.substr(0, k)) 8 | return k 9 | } 10 | } 11 | return inf.length 12 | } 13 | 14 | //find similarities between conjugations 15 | const findPrefix = function(arr) { 16 | //find the always-shared prefix (if there is one) 17 | const inf = arr[0] 18 | let len = inf.length 19 | let found = false 20 | for (let i = 1; i < arr.length; i++) { 21 | if (arr[i] !== undefined) { 22 | //a conjugation smaller than our prefix.. 23 | if (arr[i].length < inf.length) { 24 | len = arr[i].length 25 | } 26 | const pref = substring(inf, arr[i]) 27 | if (pref <= len) { 28 | found = true 29 | len = pref 30 | } 31 | } 32 | } 33 | //don't compress nothin' 34 | if (!found) { 35 | len = 0 36 | } 37 | //ok, turn it into a string with one shared prefix 38 | return inf.substr(0, len) 39 | } 40 | 41 | const pack = function(arr, prefix) { 42 | return arr.map(str => { 43 | if (str === undefined) { 44 | return '' 45 | } 46 | if (str === prefix) { 47 | return '_' 48 | } 49 | return str.substr(prefix.length, str.length) 50 | }) 51 | } 52 | 53 | const packVerbs = function(verbs) { 54 | const list = Object.keys(verbs).map(k => { 55 | let arr = [k] 56 | for (let i = 0; i < forms.length; i++) { 57 | arr.push(verbs[k][forms[i]]) 58 | } 59 | //remove the empty tail 60 | for (let i = arr.length; i > 0; i--) { 61 | if (arr[i] !== undefined) { 62 | arr = arr.slice(0, i + 1) 63 | break 64 | } 65 | } 66 | const prefix = findPrefix(arr) 67 | arr = pack(arr, prefix) 68 | const str = prefix + ':' + arr.join(',') 69 | return str 70 | }) 71 | return list.join('|') 72 | } 73 | module.exports = packVerbs 74 | -------------------------------------------------------------------------------- /scratch.js: -------------------------------------------------------------------------------- 1 | // var plg = require('./builds/compromise-plugin.js') 2 | var plg = require('./src/index') 3 | var plugin = { 4 | name: "food-plugin", 5 | words: { 6 | strawberry: 'Fruit', 7 | blueberry: 'Fruit', 8 | raspberry: 'Fruit', 9 | banana: 'Fruit', 10 | tomato: ['Fruit', 'Vegetable'], 11 | cucumber: 'Vegetable', 12 | pepper: ['Vegetable', 'Spicy'], 13 | salad: ['Fruit', 'vegetable'] 14 | }, 15 | "tags": { 16 | "Food": { 17 | "isA": "Noun", 18 | }, 19 | "Fruit": { 20 | "isA": "Food", 21 | "notA": "Vegetable" 22 | }, 23 | "Vegetable": { 24 | "isA": "Food", 25 | "notA": "Fruit" 26 | } 27 | }, 28 | "regex": { 29 | ".{3}sauce$": 'Food', 30 | ".peritif$": 'Food' 31 | }, 32 | "patterns": { 33 | "#Singular (cake|pie|pudding)": 'Dessert', 34 | "#Cardinal #Unit of [#Noun]": 'Food', 35 | "(chopped|fried|peeled) #Noun": 'Food' 36 | }, 37 | plurals: { 38 | apricot: 'apricots', 39 | banana: 'bananas', 40 | loaf: 'loaves', 41 | tooth: 'teeth', 42 | fruit: 'fruit' 43 | }, 44 | 45 | conjugations: { 46 | walk: { 47 | PastTense: 'walked', 48 | PresentTense: 'walks', 49 | }, 50 | dive: { 51 | PastTense: 'dove', 52 | Gerund: 'diving', 53 | }, 54 | arise: { 55 | PastTense: 'arose', 56 | Participle: 'arisen' 57 | }, 58 | } 59 | 60 | } 61 | plugin = { 62 | // name: 'fun', 63 | // words: { 64 | // brontosaurus: 'Dinosaur', 65 | // trex: 'Dinosaur' 66 | // }, 67 | // tags: { 68 | // Dinosaur: { 69 | // isA: 'Animal' 70 | // }, 71 | // Animal: { 72 | // isA: 'Noun' 73 | // } 74 | // }, 75 | regex: { 76 | // '.osaurus$': 'Dinosaur', 77 | '.osaurus$': ['Dinosaur', 'Animal'], 78 | // 'uuu+': 'Exaggeration' 79 | }, 80 | // plurals: { 81 | // horse: 'horses', 82 | // fly: 'flies', 83 | // jeep: 'jeeps', 84 | // one: 'one', 85 | // } 86 | }; 87 | var str = plg.pack(plugin); 88 | console.log(str) 89 | console.log('\n\n') 90 | // console.log(plg.unpack(str)) 91 | 92 | // var p2 = unpack(obj) 93 | // console.log(JSON.stringify(p2, null, 2)) 94 | // console.log('\n\n') 95 | // p2.postProcess() 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### This repo is deprecated, and is for compromise < v12. 3 | ### for the current plugin scheme, see [here](https://observablehq.com/@spencermountain/compromise-plugins) 4 | 5 | --- 6 | 7 |
8 |

:sunglasses::sunglasses::sunglasses::sunglasses:

9 |

10 | make a plugin for compromise 11 |

12 | 13 | 14 | 15 |
16 | compromise works by compressing its data in lots of weird ways. 17 | With compromise-plugin, you can ship thousands, or tens-of-thousands of custom words and patterns to the client-side, for any natural-language-processing behaviour you'd like. 18 | 19 | ```js 20 | //take this plugin, 21 | var plugin = { 22 | name:'compromise-dinosaur', 23 | words: { 24 | brontosaurus:'Dinosaur', 25 | trex: 'Dinosaur' 26 | }, 27 | tags: { 28 | Dinosaur: { 29 | isA: 'Animal' 30 | }, 31 | Animal: { 32 | isA: 'Noun' 33 | } 34 | }, 35 | regex: { 36 | '.osaurus$': 'Dinosaur', 37 | 'uuu+': 'Exaggeration' 38 | } 39 | }; 40 | 41 | var pack = require('compromise-plugin'); 42 | var tinyPlugin= pack(plugin) //CRAZY_SMALL! 43 | 44 | 45 | //then load it in nlp-compromise (it unpacks automatically) 46 | nlp.plugin(tinyPlugin); 47 | var doc = nlp('i saw a HUUUUGE trex').debug() 48 | /* 49 | 'i' - Pronoun, Noun, Singular 50 | 'saw' - PastTense, Verb, VerbPhrase 51 | 'a' - Determiner 52 | 'HUUUUGE' - Exaggeration 53 | 'trex' - Dinosaur, Animal, Noun, Singular 54 | */ 55 | ``` 56 | 57 | the cool thing about this process is that the pack() step can be as slow as we'd like. 58 | It can do all-sorts of laborious things up-front, to ensure the plugin can 'pop' back into uncompressed form very quickly. 59 | 60 | ### CLI 61 | if you prefer, you can integrate **compromise-plugin** into your workflow from the command-line: 62 | ```bash 63 | npm i -g compromise-plugin 64 | #pack it.. 65 | compromise-pack ./path/to/myPlugin.js | ./plugin.min.js 66 | 67 | #unpack it.. 68 | compromise-unpack ./path/to/plugin.min.js 69 | ``` 70 | 71 | ### Compatibility 72 | the compromise plugin spec will change over time, and to avoid having to remember which versions line-up, we will try to use the same major-version numbers as compromise. 73 | 74 | so if you want a plugin to work with comprimise v12, publish it with `compromise-plugin@12.*.*` 75 | 76 | ## Other examples 77 | ```js 78 | //military-words 79 | { 80 | plurals:{ 81 | barracks:'barracks' 82 | }, 83 | patterns:{ 84 | "#Ordinal infantry? division":'Noun', 85 | "major (general|lieutenant|#Person)":'Person', 86 | "#Posessive six":'Noun', 87 | "over$":'Expression' 88 | }, 89 | words:{ 90 | 'niner':'Value', 91 | 'buck sergeant':'Person' 92 | } 93 | } 94 | ``` 95 | 96 | MIT 97 | -------------------------------------------------------------------------------- /tests/verbs.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const both = require('../src') 3 | const pack = both.pack 4 | const unpack = both.unpack 5 | 6 | const plugin = { 7 | name: 'tryverbs', 8 | conjugations: { 9 | hear: { 10 | PastTense: 'heard', 11 | Participle: 'heard' 12 | }, 13 | hide: { 14 | PastTense: 'hid', 15 | Participle: 'hidden' 16 | }, 17 | hold: { 18 | PastTense: 'held', 19 | Participle: 'held' 20 | }, 21 | hurt: { 22 | PastTense: 'hurt', 23 | Participle: 'hurt' 24 | }, 25 | lay: { 26 | PastTense: 'laid', 27 | Participle: 'laid' 28 | }, 29 | lead: { 30 | PastTense: 'led', 31 | Participle: 'led' 32 | }, 33 | leave: { 34 | PastTense: 'left', 35 | Participle: 'left' 36 | }, 37 | lie: { 38 | PastTense: 'lay', 39 | Gerund: 'lying' 40 | }, 41 | light: { 42 | PastTense: 'lit', 43 | Participle: 'lit' 44 | }, 45 | lose: { 46 | PastTense: 'lost', 47 | Gerund: 'losing' 48 | }, 49 | make: { 50 | PastTense: 'made', 51 | Participle: 'made' 52 | }, 53 | mean: { 54 | PastTense: 'meant', 55 | Participle: 'meant' 56 | }, 57 | meet: { 58 | PastTense: 'met', 59 | Gerund: 'meeting', 60 | Participle: 'met' 61 | }, 62 | pay: { 63 | PastTense: 'paid', 64 | Participle: 'paid' 65 | }, 66 | read: { 67 | PastTense: 'read', 68 | Participle: 'read' 69 | }, 70 | ring: { 71 | PastTense: 'rang', 72 | Participle: 'rung' 73 | }, 74 | rise: { 75 | PastTense: 'rose', 76 | Gerund: 'rising', 77 | Participle: 'risen' 78 | }, 79 | run: { 80 | PastTense: 'ran', 81 | Gerund: 'running', 82 | Participle: 'run' 83 | }, 84 | say: { 85 | PastTense: 'said', 86 | PresentTense: 'says', 87 | Participle: 'said' 88 | }, 89 | see: { 90 | PastTense: 'saw', 91 | Participle: 'seen' 92 | }, 93 | sell: { 94 | PastTense: 'sold', 95 | Participle: 'sold' 96 | }, 97 | shine: { 98 | PastTense: 'shone', 99 | Participle: 'shone' 100 | }, 101 | shoot: { 102 | PastTense: 'shot', 103 | Participle: 'shot' 104 | }, 105 | show: { 106 | PastTense: 'showed' 107 | }, 108 | sing: { 109 | PastTense: 'sang', 110 | Participle: 'sung' 111 | }, 112 | sink: { 113 | PastTense: 'sank' 114 | }, 115 | sit: { 116 | PastTense: 'sat' 117 | }, 118 | slide: { 119 | PastTense: 'slid', 120 | Participle: 'slid' 121 | }, 122 | speak: { 123 | PastTense: 'spoke', 124 | Participle: 'spoken' 125 | }, 126 | spin: { 127 | PastTense: 'spun', 128 | Gerund: 'spinning', 129 | Participle: 'spun' 130 | }, 131 | stand: { 132 | PastTense: 'stood' 133 | }, 134 | steal: { 135 | PastTense: 'stole' 136 | }, 137 | stick: { 138 | PastTense: 'stuck' 139 | }, 140 | sting: { 141 | PastTense: 'stung' 142 | }, 143 | stream: {}, 144 | strike: { 145 | PastTense: 'struck', 146 | Gerund: 'striking' 147 | }, 148 | swear: { 149 | PastTense: 'swore' 150 | }, 151 | swim: { 152 | PastTense: 'swam', 153 | Gerund: 'swimming' 154 | }, 155 | swing: { 156 | PastTense: 'swung' 157 | }, 158 | teach: { 159 | PastTense: 'taught', 160 | PresentTense: 'teaches' 161 | }, 162 | tear: { 163 | PastTense: 'tore' 164 | }, 165 | tell: { 166 | PastTense: 'told' 167 | }, 168 | think: { 169 | PastTense: 'thought' 170 | } 171 | } 172 | } 173 | test('pack-verbs', function(t) { 174 | const str = pack(plugin) 175 | const unpacked = unpack(str) 176 | t.deepEqual(plugin, unpacked, 'in==out') 177 | t.equal(plugin.conjugations.tell.PastTense, unpacked.conjugations.tell.PastTense, 'tell-told') 178 | t.equal(plugin.conjugations.swim.PastTense, unpacked.conjugations.swim.PastTense, 'swim-swam') 179 | t.equal(plugin.conjugations.swim.Gerund, unpacked.conjugations.swim.Gerund, 'swim-swimming') 180 | t.end() 181 | }) 182 | --------------------------------------------------------------------------------