Merge pull request #736 from sveltejs/typescript-fixes

Typescript fixes
pull/738/head
Rich Harris 8 years ago committed by GitHub
commit a7876c737b

@ -15,22 +15,34 @@ import clone from '../utils/clone';
import DomBlock from './dom/Block'; import DomBlock from './dom/Block';
import SsrBlock from './server-side-rendering/Block'; import SsrBlock from './server-side-rendering/Block';
import Stylesheet from '../css/Stylesheet'; import Stylesheet from '../css/Stylesheet';
import { Node, Parsed, CompileOptions } from '../interfaces'; import { Node, GenerateOptions, Parsed, CompileOptions } from '../interfaces';
const test = typeof global !== 'undefined' && global.__svelte_test; const test = typeof global !== 'undefined' && global.__svelte_test;
interface Computation {
key: string;
deps: string[]
}
export default class Generator { export default class Generator {
ast: Parsed;
parsed: Parsed; parsed: Parsed;
source: string; source: string;
name: string; name: string;
options: CompileOptions; options: CompileOptions;
defaultExport: Node[];
imports: Node[]; imports: Node[];
helpers: Set<string>; helpers: Set<string>;
components: Set<string>; components: Set<string>;
events: Set<string>; events: Set<string>;
transitions: Set<string>; transitions: Set<string>;
importedComponents: Map<string, string>; importedComponents: Map<string, string>;
namespace: string;
hasComponents: boolean;
hasJs: boolean;
computations: Computation[];
templateProperties: Record<string, Node>;
code: MagicString; code: MagicString;
@ -78,9 +90,6 @@ export default class Generator {
// styles // styles
this.stylesheet = stylesheet; this.stylesheet = stylesheet;
// TODO this is legacy — just to get the tests to pass during the transition
this.css = this.stylesheet.render(options.cssOutputFilename).css;
// allow compiler to deconflict user's `import { get } from 'whatever'` and // allow compiler to deconflict user's `import { get } from 'whatever'` and
// Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`; // Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`;
this.importedNames = new Set(); this.importedNames = new Set();
@ -263,7 +272,7 @@ export default class Generator {
return (expression._dependencies = dependencies); return (expression._dependencies = dependencies);
} }
generate(result, options: CompileOptions, { name, format }) { generate(result: string, options: CompileOptions, { name, format }: GenerateOptions ) {
if (this.imports.length) { if (this.imports.length) {
const statements: string[] = []; const statements: string[] = [];
@ -381,9 +390,9 @@ export default class Generator {
return alias; return alias;
} }
getUniqueNameMaker(params) { getUniqueNameMaker(params: string[]) {
const localUsedNames = new Set(params); const localUsedNames = new Set(params);
return name => { return (name: string) => {
if (test) name = `${name}$`; if (test) name = `${name}$`;
let alias = name; let alias = name;
for ( for (
@ -404,8 +413,8 @@ export default class Generator {
const { js } = this.parsed; const { js } = this.parsed;
const imports = this.imports; const imports = this.imports;
const computations = []; const computations: Computation[] = [];
const templateProperties = {}; const templateProperties: Record<string, Node> = {};
let namespace = null; let namespace = null;
let hasJs = !!js; let hasJs = !!js;
@ -439,7 +448,7 @@ export default class Generator {
['helpers', 'events', 'components', 'transitions'].forEach(key => { ['helpers', 'events', 'components', 'transitions'].forEach(key => {
if (templateProperties[key]) { if (templateProperties[key]) {
templateProperties[key].value.properties.forEach((prop: node) => { templateProperties[key].value.properties.forEach((prop: Node) => {
this[key].add(prop.key.name); this[key].add(prop.key.name);
}); });
} }
@ -461,7 +470,7 @@ export default class Generator {
const visited = new Set(); const visited = new Set();
function visit(key) { const visit = function visit(key: string) {
if (!dependencies.has(key)) return; // not a computation if (!dependencies.has(key)) return; // not a computation
if (visited.has(key)) return; if (visited.has(key)) return;

@ -317,7 +317,7 @@ export default function dom(
let scope = annotateWithScopes(expression); let scope = annotateWithScopes(expression);
walk(expression, { walk(expression, {
enter(node, parent) { enter(node: Node, parent: Node) {
if (node._scope) scope = node._scope; if (node._scope) scope = node._scope;
if ( if (
@ -337,7 +337,7 @@ export default function dom(
} }
}, },
leave(node) { leave(node: Node) {
if (node._scope) scope = scope.parent; if (node._scope) scope = scope.parent;
}, },
}); });

@ -1,5 +1,5 @@
export interface State { export interface State {
name: string; name?: string;
namespace: string; namespace: string;
parentNode: string; parentNode: string;
parentNodes: string; parentNodes: string;

@ -1,8 +1,7 @@
import { Declaration, Options } from './getIntro'; import { CompileOptions, Node } from '../../../interfaces';
export type Globals = (id: string) => any;
export default function getGlobals(imports: Declaration[], options: Options) { export default function getGlobals(imports: Node[], options: CompileOptions) {
const { globals, onerror, onwarn } = options; const { globals, onerror, onwarn } = options;
const globalFn = getGlobalFn(globals); const globalFn = getGlobalFn(globals);
@ -40,7 +39,7 @@ export default function getGlobals(imports: Declaration[], options: Options) {
}); });
} }
function getGlobalFn(globals: any): Globals { function getGlobalFn(globals: any): (id: string) => string {
if (typeof globals === 'function') return globals; if (typeof globals === 'function') return globals;
if (typeof globals === 'object') { if (typeof globals === 'object') {
return id => globals[id]; return id => globals[id];

@ -1,29 +1,11 @@
import deindent from '../../../utils/deindent'; import deindent from '../../../utils/deindent';
import getGlobals, { Globals } from './getGlobals'; import getGlobals from './getGlobals';
import { CompileOptions, ModuleFormat, Node } from '../../../interfaces';
export type ModuleFormat = 'es' | 'amd' | 'cjs' | 'iife' | 'umd' | 'eval';
export interface Options {
name: string;
amd?: {
id?: string;
};
globals: Globals | object;
onerror: (err: Error) => void;
onwarn: (obj: Error | { message: string }) => void;
}
export interface Declaration {
name: string;
source: {
value: string;
};
}
export default function getIntro( export default function getIntro(
format: ModuleFormat, format: ModuleFormat,
options: Options, options: CompileOptions,
imports: Declaration[] imports: Node[]
) { ) {
if (format === 'es') return ''; if (format === 'es') return '';
if (format === 'amd') return getAmdIntro(options, imports); if (format === 'amd') return getAmdIntro(options, imports);
@ -35,7 +17,7 @@ export default function getIntro(
throw new Error(`Not implemented: ${format}`); throw new Error(`Not implemented: ${format}`);
} }
function getAmdIntro(options: Options, imports: Declaration[]) { function getAmdIntro(options: CompileOptions, imports: Node[]) {
const sourceString = imports.length const sourceString = imports.length
? `[ ${imports ? `[ ${imports
.map(declaration => `'${removeExtension(declaration.source.value)}'`) .map(declaration => `'${removeExtension(declaration.source.value)}'`)
@ -49,7 +31,7 @@ function getAmdIntro(options: Options, imports: Declaration[]) {
: ''}${sourceString}function (${paramString(imports)}) { 'use strict';\n\n`; : ''}${sourceString}function (${paramString(imports)}) { 'use strict';\n\n`;
} }
function getCjsIntro(options: Options, imports: Declaration[]) { function getCjsIntro(options: CompileOptions, imports: Node[]) {
const requireBlock = imports const requireBlock = imports
.map( .map(
declaration => declaration =>
@ -64,7 +46,7 @@ function getCjsIntro(options: Options, imports: Declaration[]) {
return `'use strict';\n\n`; return `'use strict';\n\n`;
} }
function getIifeIntro(options: Options, imports: Declaration[]) { function getIifeIntro(options: CompileOptions, imports: Node[]) {
if (!options.name) { if (!options.name) {
throw new Error(`Missing required 'name' option for IIFE export`); throw new Error(`Missing required 'name' option for IIFE export`);
} }
@ -74,7 +56,7 @@ function getIifeIntro(options: Options, imports: Declaration[]) {
)}) { 'use strict';\n\n`; )}) { 'use strict';\n\n`;
} }
function getUmdIntro(options: Options, imports: Declaration[]) { function getUmdIntro(options: CompileOptions, imports: Node[]) {
if (!options.name) { if (!options.name) {
throw new Error(`Missing required 'name' option for UMD export`); throw new Error(`Missing required 'name' option for UMD export`);
} }
@ -101,11 +83,11 @@ function getUmdIntro(options: Options, imports: Declaration[]) {
); );
} }
function getEvalIntro(options: Options, imports: Declaration[]) { function getEvalIntro(options: CompileOptions, imports: Node[]) {
return `(function (${paramString(imports)}) { 'use strict';\n\n`; return `(function (${paramString(imports)}) { 'use strict';\n\n`;
} }
function paramString(imports: Declaration[]) { function paramString(imports: Node[]) {
return imports.length ? ` ${imports.map(dep => dep.name).join(', ')} ` : ''; return imports.length ? ` ${imports.map(dep => dep.name).join(', ')} ` : '';
} }

@ -1,10 +1,11 @@
import getGlobals from './getGlobals'; import getGlobals from './getGlobals';
import { CompileOptions, Node } from '../../../interfaces';
export default function getOutro( export default function getOutro(
format: string, format: string,
name: string, name: string,
options, options: CompileOptions,
imports imports: Node[]
) { ) {
if (format === 'es') { if (format === 'es') {
return `export default ${name};`; return `export default ${name};`;

@ -1,6 +1,6 @@
import { Node } from '../../../interfaces'; import { Node, Visitor } from '../../../interfaces';
export default function walkHtml(html: Node, visitors) { export default function walkHtml(html: Node, visitors: Record<string, Visitor>) {
function visit(node: Node) { function visit(node: Node) {
const visitor = visitors[node.type]; const visitor = visitors[node.type];
if (!visitor) throw new Error(`Not implemented: ${node.type}`); if (!visitor) throw new Error(`Not implemented: ${node.type}`);

@ -34,11 +34,17 @@ export interface Warning {
toString: () => string; toString: () => string;
} }
export type ModuleFormat = 'es' | 'amd' | 'cjs' | 'iife' | 'umd' | 'eval';
export interface CompileOptions { export interface CompileOptions {
format?: string; format?: ModuleFormat;
name?: string; name?: string;
filename?: string; filename?: string;
generate?: string; generate?: string;
globals?: ((id: string) => string) | object;
amd?: {
id?: string;
};
outputFilename?: string; outputFilename?: string;
cssOutputFilename?: string; cssOutputFilename?: string;
@ -51,3 +57,13 @@ export interface CompileOptions {
onerror?: (error: Error) => void; onerror?: (error: Error) => void;
onwarn?: (warning: Warning) => void; onwarn?: (warning: Warning) => void;
} }
export interface GenerateOptions {
name: string;
format: ModuleFormat;
}
export interface Visitor {
enter: (node: Node) => void;
leave?: (node: Node) => void;
}

@ -2,7 +2,7 @@ import { parseExpressionAt } from 'acorn';
import spaces from '../../utils/spaces'; import spaces from '../../utils/spaces';
import { Parser } from '../index'; import { Parser } from '../index';
function readExpression(parser: Parser, start: number, quoteMark) { function readExpression(parser: Parser, start: number, quoteMark: string|null) {
let str = ''; let str = '';
let escaped = false; let escaped = false;

@ -1,10 +1,11 @@
import { parse } from 'acorn'; import { parse } from 'acorn';
import spaces from '../../utils/spaces'; import spaces from '../../utils/spaces';
import { Parser } from '../index'; import { Parser } from '../index';
import { Node } from '../../interfaces';
const scriptClosingTag = '</script>'; const scriptClosingTag = '</script>';
export default function readScript(parser: Parser, start: number, attributes) { export default function readScript(parser: Parser, start: number, attributes: Node[]) {
const scriptStart = parser.index; const scriptStart = parser.index;
const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart); const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart);

@ -1,8 +1,9 @@
import parse from 'css-tree/lib/parser/index.js'; import parse from 'css-tree/lib/parser/index.js';
import walk from 'css-tree/lib/utils/walk.js'; import walk from 'css-tree/lib/utils/walk.js';
import { Parser } from '../index'; import { Parser } from '../index';
import { Node } from '../../interfaces';
export default function readStyle(parser: Parser, start: number, attributes) { export default function readStyle(parser: Parser, start: number, attributes: Node[]) {
const contentStart = parser.index; const contentStart = parser.index;
const styles = parser.readUntil(/<\/style>/); const styles = parser.readUntil(/<\/style>/);
const contentEnd = parser.index; const contentEnd = parser.index;
@ -23,7 +24,7 @@ export default function readStyle(parser: Parser, start: number, attributes) {
} }
// tidy up AST // tidy up AST
walk.all(ast, node => { walk.all(ast, (node: Node) => {
if (node.loc) { if (node.loc) {
node.start = node.loc.start.offset; node.start = node.loc.start.offset;
node.end = node.loc.end.offset; node.end = node.loc.end.offset;

@ -6,7 +6,7 @@ import { Node } from '../../interfaces';
const validIdentifier = /[a-zA-Z_$][a-zA-Z0-9_$]*/; const validIdentifier = /[a-zA-Z_$][a-zA-Z0-9_$]*/;
function trimWhitespace(block, trimBefore, trimAfter) { function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) {
const firstChild = block.children[0]; const firstChild = block.children[0];
const lastChild = block.children[block.children.length - 1]; const lastChild = block.children[block.children.length - 1];
@ -220,6 +220,4 @@ export default function mustache(parser: Parser) {
expression, expression,
}); });
} }
return null;
} }

@ -78,7 +78,7 @@ export default function tag(parser: Parser) {
data, data,
}); });
return null; return;
} }
const isClosingTag = parser.eat('/'); const isClosingTag = parser.eat('/');
@ -133,7 +133,7 @@ export default function tag(parser: Parser) {
parent.end = parser.index; parent.end = parser.index;
parser.stack.pop(); parser.stack.pop();
return null; return;
} else if (disallowedContents.has(parent.name)) { } else if (disallowedContents.has(parent.name)) {
// can this be a child of the parent element, or does it implicitly // can this be a child of the parent element, or does it implicitly
// close it, like `<li>one<li>two`? // close it, like `<li>one<li>two`?
@ -200,8 +200,6 @@ export default function tag(parser: Parser) {
// don't push self-closing elements onto the stack // don't push self-closing elements onto the stack
parser.stack.push(element); parser.stack.push(element);
} }
return null;
} }
function readTagName(parser: Parser) { function readTagName(parser: Parser) {
@ -242,7 +240,7 @@ function readTagName(parser: Parser) {
return name; return name;
} }
function readAttribute(parser: Parser, uniqueNames) { function readAttribute(parser: Parser, uniqueNames: Set<string>) {
const start = parser.index; const start = parser.index;
let name = parser.readUntil(/(\s|=|\/|>)/); let name = parser.readUntil(/(\s|=|\/|>)/);

@ -20,6 +20,4 @@ export default function text(parser: Parser) {
type: 'Text', type: 'Text',
data: decodeCharacterReferences(data), data: decodeCharacterReferences(data),
}); });
return null;
} }

@ -1,7 +1,7 @@
import { Node } from '../interfaces'; import { Node, Parsed } from '../interfaces';
export default function clone(node: Node) { export default function clone(node: Node|Parsed) {
const cloned = {}; const cloned: any = {};
for (const key in node) { for (const key in node) {
const value = node[key]; const value = node[key];

@ -20,4 +20,6 @@ export const validNamespaces = [
xmlns, xmlns,
]; ];
export default { html, mathml, svg, xlink, xml, xmlns }; const namespaces: Record<string, string> = { html, mathml, svg, xlink, xml, xmlns };
export default namespaces;

@ -1,3 +1,4 @@
import MagicString from 'magic-string';
import { Node } from '../interfaces'; import { Node } from '../interfaces';
const keys = { const keys = {
@ -10,7 +11,7 @@ const offsets = {
Program: [0, 0], Program: [0, 0],
}; };
export function removeNode(code, parent: Node, node: Node) { export function removeNode(code: MagicString, parent: Node, node: Node) {
const key = keys[parent.type]; const key = keys[parent.type];
const offset = offsets[parent.type]; const offset = offsets[parent.type];
if (!key || !offset) throw new Error(`not implemented: ${parent.type}`); if (!key || !offset) throw new Error(`not implemented: ${parent.type}`);
@ -44,7 +45,7 @@ export function removeNode(code, parent: Node, node: Node) {
return; return;
} }
export function removeObjectKey(code, node, key) { export function removeObjectKey(code: MagicString, node: Node, key: string) {
if (node.type !== 'ObjectExpression') return; if (node.type !== 'ObjectExpression') return;
let i = node.properties.length; let i = node.properties.length;

@ -1,28 +1,28 @@
// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js // adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js
// BSD Licensed // BSD Licensed
export default function FuzzySet( const GRAM_SIZE_LOWER = 2;
arr, const GRAM_SIZE_UPPER = 3;
useLevenshtein,
gramSizeLower,
gramSizeUpper
) {
// default options
arr = arr || [];
this.gramSizeLower = gramSizeLower || 2;
this.gramSizeUpper = gramSizeUpper || 3;
this.useLevenshtein = typeof useLevenshtein !== 'boolean'
? true
: useLevenshtein;
// define all the object functions and attributes // return an edit distance from 0 to 1
this.exactSet = {}; function _distance(str1: string, str2: string) {
this.matchDict = {}; if (str1 === null && str2 === null)
this.items = {}; throw 'Trying to compare two null values';
if (str1 === null || str2 === null) return 0;
str1 = String(str1);
str2 = String(str2);
const distance = levenshtein(str1, str2);
if (str1.length > str2.length) {
return 1 - distance / str1.length;
} else {
return 1 - distance / str2.length;
}
}
// helper functions // helper functions
function levenshtein(str1, str2) { function levenshtein(str1: string, str2: string) {
const current = []; const current: number[] = [];
let prev; let prev;
let value; let value;
@ -44,27 +44,11 @@ export default function FuzzySet(
} }
return current.pop(); return current.pop();
} }
// return an edit distance from 0 to 1
function _distance(str1, str2) {
if (str1 === null && str2 === null)
throw 'Trying to compare two null values';
if (str1 === null || str2 === null) return 0;
str1 = String(str1);
str2 = String(str2);
const distance = levenshtein(str1, str2);
if (str1.length > str2.length) {
return 1 - distance / str1.length;
} else {
return 1 - distance / str2.length;
}
}
const _nonWordRe = /[^\w, ]+/; const _nonWordRe = /[^\w, ]+/;
function _iterateGrams(value, gramSize) { function _iterateGrams(value: string, gramSize: number) {
gramSize = gramSize || 2; gramSize = gramSize || 2;
const simplified = '-' + value.toLowerCase().replace(_nonWordRe, '') + '-'; const simplified = '-' + value.toLowerCase().replace(_nonWordRe, '') + '-';
const lenDiff = gramSize - simplified.length; const lenDiff = gramSize - simplified.length;
@ -79,9 +63,9 @@ export default function FuzzySet(
results.push(simplified.slice(i, i + gramSize)); results.push(simplified.slice(i, i + gramSize));
} }
return results; return results;
} }
function _gramCounter(value, gramSize) { function _gramCounter(value: string, gramSize: number) {
// return an object where key=gram, value=number of occurrences // return an object where key=gram, value=number of occurrences
gramSize = gramSize || 2; gramSize = gramSize || 2;
const result = {}; const result = {};
@ -96,20 +80,73 @@ export default function FuzzySet(
} }
} }
return result; return result;
}
function sortDescending(a, b) {
return b[0] - a[0];
}
export default class FuzzySet {
exactSet: object;
matchDict: object;
items: object;
constructor(arr: string[]) {
// define all the object functions and attributes
this.exactSet = {};
this.matchDict = {};
this.items = {};
// initialization
for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
this.items[i] = [];
}
// add all the items to the set
for (let i = 0; i < arr.length; ++i) {
this.add(arr[i]);
}
} }
// the main functions add(value: string) {
this.get = function(value, defaultValue) { const normalizedValue = value.toLowerCase();
// check for value in set, returning defaultValue or null if none found if (normalizedValue in this.exactSet) {
const result = this._get(value); return false;
if (!result && typeof defaultValue !== 'undefined') {
return defaultValue;
} }
return result;
let i = GRAM_SIZE_LOWER;
for (i; i < GRAM_SIZE_UPPER + 1; ++i) {
this._add(value, i);
}
}
_add(value: string, gramSize: number) {
const normalizedValue = value.toLowerCase();
const items = this.items[gramSize] || [];
const index = items.length;
items.push(0);
const gramCounts = _gramCounter(normalizedValue, gramSize);
let sumOfSquareGramCounts = 0;
let gram;
let gramCount;
for (gram in gramCounts) {
gramCount = gramCounts[gram];
sumOfSquareGramCounts += Math.pow(gramCount, 2);
if (gram in this.matchDict) {
this.matchDict[gram].push([index, gramCount]);
} else {
this.matchDict[gram] = [[index, gramCount]];
}
}
const vectorNormal = Math.sqrt(sumOfSquareGramCounts);
items[index] = [vectorNormal, normalizedValue];
this.items[gramSize] = items;
this.exactSet[normalizedValue] = value;
}; };
this._get = function(value) { get(value: string) {
const normalizedValue = this._normalizeStr(value); const normalizedValue = value.toLowerCase();
const result = this.exactSet[normalizedValue]; const result = this.exactSet[normalizedValue];
if (result) { if (result) {
@ -119,8 +156,8 @@ export default function FuzzySet(
let results = []; let results = [];
// start with high gram size and if there are no results, go to lower gram sizes // start with high gram size and if there are no results, go to lower gram sizes
for ( for (
let gramSize = this.gramSizeUpper; let gramSize = GRAM_SIZE_UPPER;
gramSize >= this.gramSizeLower; gramSize >= GRAM_SIZE_LOWER;
--gramSize --gramSize
) { ) {
results = this.__get(value, gramSize); results = this.__get(value, gramSize);
@ -129,10 +166,10 @@ export default function FuzzySet(
} }
} }
return null; return null;
}; }
this.__get = function(value, gramSize) { __get(value: string, gramSize: number) {
const normalizedValue = this._normalizeStr(value); const normalizedValue = value.toLowerCase();
const matches = {}; const matches = {};
const gramCounts = _gramCounter(normalizedValue, gramSize); const gramCounts = _gramCounter(normalizedValue, gramSize);
const items = this.items[gramSize]; const items = this.items[gramSize];
@ -159,17 +196,6 @@ export default function FuzzySet(
} }
} }
function isEmptyObject(obj) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) return false;
}
return true;
}
if (isEmptyObject(matches)) {
return null;
}
const vectorNormal = Math.sqrt(sumOfSquareGramCounts); const vectorNormal = Math.sqrt(sumOfSquareGramCounts);
let results = []; let results = [];
let matchScore; let matchScore;
@ -182,19 +208,10 @@ export default function FuzzySet(
items[matchIndex][1], items[matchIndex][1],
]); ]);
} }
function sortDescending(a, b) {
if (a[0] < b[0]) {
return 1;
} else if (a[0] > b[0]) {
return -1;
} else {
return 0;
}
}
results.sort(sortDescending); results.sort(sortDescending);
if (this.useLevenshtein) {
const newResults = []; let newResults = [];
const endIndex = Math.min(50, results.length); const endIndex = Math.min(50, results.length);
// truncate somewhat arbitrarily to 50 // truncate somewhat arbitrarily to 50
for (let i = 0; i < endIndex; ++i) { for (let i = 0; i < endIndex; ++i) {
@ -205,104 +222,14 @@ export default function FuzzySet(
} }
results = newResults; results = newResults;
results.sort(sortDescending); results.sort(sortDescending);
}
const newResults = []; newResults = [];
for (let i = 0; i < results.length; ++i) { for (let i = 0; i < results.length; ++i) {
if (results[i][0] == results[0][0]) { if (results[i][0] == results[0][0]) {
newResults.push([results[i][0], this.exactSet[results[i][1]]]); newResults.push([results[i][0], this.exactSet[results[i][1]]]);
} }
} }
return newResults;
};
this.add = function(value) {
const normalizedValue = this._normalizeStr(value);
if (normalizedValue in this.exactSet) {
return false;
}
let i = this.gramSizeLower;
for (i; i < this.gramSizeUpper + 1; ++i) {
this._add(value, i);
}
};
this._add = function(value, gramSize) {
const normalizedValue = this._normalizeStr(value);
const items = this.items[gramSize] || [];
const index = items.length;
items.push(0);
const gramCounts = _gramCounter(normalizedValue, gramSize);
let sumOfSquareGramCounts = 0;
let gram;
let gramCount;
for (gram in gramCounts) {
gramCount = gramCounts[gram];
sumOfSquareGramCounts += Math.pow(gramCount, 2);
if (gram in this.matchDict) {
this.matchDict[gram].push([index, gramCount]);
} else {
this.matchDict[gram] = [[index, gramCount]];
}
}
const vectorNormal = Math.sqrt(sumOfSquareGramCounts);
items[index] = [vectorNormal, normalizedValue];
this.items[gramSize] = items;
this.exactSet[normalizedValue] = value;
};
this._normalizeStr = function(str) {
if (Object.prototype.toString.call(str) !== '[object String]')
throw 'Must use a string as argument to FuzzySet functions';
return str.toLowerCase();
};
// return length of items in set
this.length = function() {
let count = 0;
let prop;
for (prop in this.exactSet) {
if (this.exactSet.hasOwnProperty(prop)) {
count += 1;
}
}
return count;
};
// return is set is empty
this.isEmpty = function() {
for (const prop in this.exactSet) {
if (this.exactSet.hasOwnProperty(prop)) {
return false;
}
}
return true;
};
// return list of values loaded into set
this.values = function() {
const values = [];
for (const prop in this.exactSet) { return newResults;
if (this.exactSet.hasOwnProperty(prop)) {
values.push(this.exactSet[prop]);
}
}
return values;
}; };
// initialization
let i = this.gramSizeLower;
for (i; i < this.gramSizeUpper + 1; ++i) {
this.items[i] = [];
}
// add all the items to the set
for (i = 0; i < arr.length; ++i) {
this.add(arr[i]);
}
return this;
} }
Loading…
Cancel
Save