diff --git a/bin/firebase-bolt b/bin/firebase-bolt index 4da2216..75403d3 100755 --- a/bin/firebase-bolt +++ b/bin/firebase-bolt @@ -72,7 +72,6 @@ function main() { log("Can only compile a single file."); usage(1); } - // Read Bolt file from stdin if (args._.length === 0) { if (process.stdin.isTTY) { @@ -82,6 +81,8 @@ function main() { if (args.output !== undefined) { writeTranslation(util.ensureExtension(args.output, 'json'), data); } else { + // Note: Not sure but this will more than likely break some form of + // compatability console.log(translateRules(data)); } }); @@ -106,10 +107,14 @@ function main() { log("Could not read file: " + inFile); process.exit(1); } - writeTranslation(outFile, data); + writeTranslationWithImports(outFile, data, inFile); }); } - +/** +* Depricated: Use writeTranslation still needs to be maintained as we have the +* ability to write the translation from STDIN not just the file system. +* ??? Global modules should still be supported from std in. +*/ function writeTranslation(outFile, data) { log("Generating " + outFile + "..."); fs.writeFile(outFile, translateRules(data) + '\n', 'utf8', function(err2) { @@ -120,6 +125,16 @@ function writeTranslation(outFile, data) { }); } +function writeTranslationWithImports(outFile, data, inFile) { + log("Generating " + outFile + "..."); + fs.writeFile(outFile, translateRulesWithImports(inFile) + '\n', 'utf8', function(err2) { + if (err2) { + log("Could not write file: " + outFile); + process.exit(1); + } + }); +} + function usage(code) { var cmdName = process.argv[1].split('/').slice(-1); console.error("Translate Firebase Bolt file into JSON rules format.\n"); @@ -160,10 +175,10 @@ function readFile(f, callback) { function translateRules(input) { var symbols; - var rules; try { symbols = bolt.parse(input); + return continueTranslateRules(symbols); } catch (e) { if (DEBUG) { log(e.stack); @@ -171,7 +186,24 @@ function translateRules(input) { log(e.message, e.line, e.column); process.exit(1); } +} +function translateRulesWithImports(inFile) { + var symbols; + + try { + symbols = bolt.parseWithImports(inFile); + return continueTranslateRules(symbols); + } catch (e) { + if (DEBUG) { + log(e.stack); + } + log(e.message, e.line, e.column); + process.exit(1); + } +} +function continueTranslateRules(symbols) { + var rules; try { var gen = new bolt.Generator(symbols); rules = gen.generateRules(); diff --git a/docs/guide.md b/docs/guide.md index c334bda..3ac4efd 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -310,6 +310,61 @@ isCurrentUser(uid) { auth != null && auth.uid == uid } } ``` +## Imports + +Local & Global type imports are supported. + +### Global Module + + import * from {'module'} + +Each modules is assumed to have an entry point under the 'node_modules/firebase-bolt-module/index.bolt' filename and will be processed from here. + +Example usages of global modules + + $npm install 'firebase-bolt-validation' + + import {'firebase-bolt-validation'} + +### Local Modules + + import * from {'./module'} + import * as valTypes from {'./validation-types'} + import {foo} from {'./bar'} + +### Examples + + +```javascript + // Module.bolt + import * from {'./Foo'} + import {Foo} from {'./Foo'} + import * as alias form {'./Foo'} + + type Test extends Bar { + validate() { this.test(/^[0-9]*$/) } + } + + type Example extends alias.Bar { + id: String + } + + // Note: No duplicate import detection + type ExampleWithRestrictions extends Foo { + id: string + } + + // Foo.bolt + type Bar { + validate() {this.test(/^[a-z0-9]$/) } + child: String + } + + type Foo { + when: String + } +``` + # Bolt Cookbook The rest of this guide will provide sample recipes to solve typical problems that developers diff --git a/gulpfile.js b/gulpfile.js index 6ea86b0..b7f77cb 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -91,7 +91,7 @@ gulp.task('ts-compile', ['build-peg'], function() { .pipe(gulp.dest(LIB_DIR)); }); -gulp.task('build', ['ts-compile', 'browserify-bolt']); +gulp.task('build', ['build-peg', 'ts-compile', 'browserify-bolt']); gulp.task('build-peg', function() { return gulp.src('src/rules-parser.pegjs') diff --git a/package.json b/package.json index 2837991..487c494 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "browserify": "^11.0.1", "del": "^2.0.2", "eslint": "^1.1.0", + "firebase-bolt-validation": "0.0.3", "gulp": "^3.9.0", "gulp-eslint": "^1.0.0", "gulp-mocha": "^2.1.3", diff --git a/samples/subModule/typeOne.bolt b/samples/subModule/typeOne.bolt new file mode 100644 index 0000000..b9a9458 --- /dev/null +++ b/samples/subModule/typeOne.bolt @@ -0,0 +1,24 @@ +import {TypeTwo} from './typeTwo' +import {Email} as global from 'firebase-bolt-validation' + +type SampleOne extends TypeTwo { + validate() { true } +} +type TypeOne extends String { + validate() { true } +} +type Alpha extends String { + validate() { this.test(/^[a-zA-Z]*$/) } +} + +type Alphanumeric extends global.Email { + +} + +type Ascii extends String { + validate() { this.test(/^[\x00-\x7F]+$/) } +} + +type Secondary extends String { + validate() { true } +} diff --git a/samples/subModule/typeTwo.bolt b/samples/subModule/typeTwo.bolt new file mode 100644 index 0000000..8ad645a --- /dev/null +++ b/samples/subModule/typeTwo.bolt @@ -0,0 +1,8 @@ + +type TypeTwo extends String { + validate() { this.test(/^[a-z]*$/) } +} + +type Fourth extends String { + validate() { this.test(/^4$/)} +} diff --git a/samples/type-imports.bolt b/samples/type-imports.bolt new file mode 100644 index 0000000..4c3c86b --- /dev/null +++ b/samples/type-imports.bolt @@ -0,0 +1,40 @@ +// Lvl 1 - simply import everything. +import * from './subModule/typeOne' +// Lvl 2 - import single item only +import {Alpha, Alphanumeric} from './subModule/typeOne' +// Lvl 3 - import as an aliased type +import {Alpha, Alphanumeric} as types from './subModule/typeOne' +// Lvl 4 - import recursive types +import { SampleOne } from './subModule/typeOne' + +// Lvl 5 - import with an adjusted path +// import from './subModule/typeOne' at '/somewhere' // duplicate import test + + +type Test extends Another { + validate() { this.test(/^[0-9]*$/) } +} + +type Another { + validate() { this.message.length < 14 } + something : String +} +path / is SampleOne { + validate() { true } +} + +path /test is Alphanumeric { + read() {true} +} + +path /alpha is Alpha { + read() {true} +} + +path /alphanumeric is types.Alphanumeric { + read() { true } +} + +path /ascii is Ascii { + read() { true } +} diff --git a/src/ast.ts b/src/ast.ts index 206c02e..7da36ad 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -79,6 +79,7 @@ export interface TypeParams { [name: string]: ExpType; }; // Simple Type (reference) export interface ExpSimpleType extends Exp { name: string; + namespace?: string; } // Union Type: Type1 | Type2 | ... @@ -97,6 +98,13 @@ export interface Method { body: Exp; } +export interface Import { + filename: string; + alias: string; + identifiers: string[]; + symbols?: Symbols; +} + export class PathPart { label: string; variable: string; @@ -501,6 +509,14 @@ export function method(params: string[], body: Exp): Method { }; } +export function typeTypeNamespaced(typeName: string, namespace: string) { + if (namespace) { + return { type: "type", valueType: "type", name: typeName, namespace: namespace}; + } else { + return { type: "type", valueType: "type", name: typeName}; // backwards compatability for tests + } +} + export function typeType(typeName: string): ExpSimpleType { return { type: "type", valueType: "type", name: typeName }; } @@ -509,6 +525,9 @@ export function unionType(types: ExpType[]): ExpUnionType { return { type: "union", valueType: "type", types: types }; } +export function genericTypeNamespaced(typeName: string, params: ExpType[], namespace: string) { + return { type: "generic", valueType: "type", name: typeName, params: params, namespace: namespace }; +} export function genericType(typeName: string, params: ExpType[]): ExpGenericType { return { type: "generic", valueType: "type", name: typeName, params: params }; } @@ -517,11 +536,13 @@ export class Symbols { functions: { [name: string]: Method }; paths: Path[]; schema: { [name: string]: Schema }; + imports: Import[] ; constructor() { this.functions = {}; this.paths = []; this.schema = {}; + this.imports = []; } register(map: {[name: string]: T}, typeName: string, name: string, object: T): T { @@ -538,6 +559,16 @@ export class Symbols { method(params, body)); } + registerImport(identifiers: string[], alias: string, filePath: string): Import { + var i: Import = { + filename : filePath, + alias: alias, + identifiers: identifiers + }; + this.imports.push(i); + return i; + } + registerPath(template: PathTemplate, isType: ExpType | void, methods: { [name: string]: Method; } = {}): Path { isType = isType || typeType('Any'); var p: Path = { diff --git a/src/bolt.ts b/src/bolt.ts index 63203ce..8de7091 100644 --- a/src/bolt.ts +++ b/src/bolt.ts @@ -19,14 +19,16 @@ if (typeof Promise === 'undefined') { require('es6-promise').polyfill(); } -let parser = require('./rules-parser'); +let parser = require('./imports-parser'); +let rulesParser = require('./rules-parser'); import * as generator from './rules-generator'; import * as astImport from './ast'; export let FILE_EXTENSION = 'bolt'; export let ast = astImport; -export let parse = parser.parse; +export let parseWithImports = parser.parseWithImports; +export let parse = rulesParser.parse; export let Generator = generator.Generator; export let decodeExpression = ast.decodeExpression; export let generate = generator.generate; diff --git a/src/imports-parser.ts b/src/imports-parser.ts new file mode 100644 index 0000000..fe53b6a --- /dev/null +++ b/src/imports-parser.ts @@ -0,0 +1,55 @@ +let rulesParser = require('./rules-parser'); +let fs = require('fs'); +/* + Imports file parser for split file systems + Note: Using a modified ES6 syntax to include imports +*/ +export function parseWithImports(filename: string) { + // creating a stream through which each file will pass + let contents = fs.readFileSync(filename, "utf8"); + return parserWrapper(contents, filename); +} + +/* + *************************** Function Section **************************** +*/ + +/* ******** wrapper for recursive parsing ******* */ +function parserWrapper(data: string, filename: string) { + var sym = rulesParser.parse(data); + + // Process any imports in symbol list + for (let i = 0; i < sym.imports.length; i++) { + let next = sym.imports[i]; + var nextFilename = getNextFilenameFromContextAndImport(filename, next.filename); + let contents = fs.readFileSync(nextFilename, 'utf8'); + let nextSymbols = parserWrapper(contents, nextFilename); + sym.imports[i].symbols = nextSymbols; // add it to the next part of the tree + } + return sym; +}; // end function + +// Convert absolute filenames to relative +// Convert relative filenames to include original path +function getNextFilenameFromContextAndImport(current: string, nextImport: any) { + current = current.replace('.bolt', ''); + nextImport = nextImport.replace('.bolt', ''); + var currentFn = current.split('/'); + var nextFn = nextImport.split('/'); + let result = ''; + if (nextFn[0] !== '.' && nextFn[0] !== '..') { // global reference + result = './node_modules/' + nextImport + '/index.bolt'; + } else { + // import {./something} -> ['.','something'] -> '' + // import {./something/anotherthing} -> ['.','something','anotherthing'] -> something + currentFn.pop(); // remove trailing file name and leave only the directory + nextFn = currentFn.concat(nextFn); + // if file.bolt exists then we have it otherwise return + if (fs.existsSync(nextFn.join('/') + '.bolt')) { + result = nextFn.join('/') + '.bolt'; + } else { + result = nextFn.join('/') + '/index.bolt'; + } + } + return result; +} diff --git a/src/rules-generator.ts b/src/rules-generator.ts index 25de82d..72918d8 100644 --- a/src/rules-generator.ts +++ b/src/rules-generator.ts @@ -16,9 +16,11 @@ import * as util from './util'; import * as ast from './ast'; import {warn, error} from './logger'; -let parser = require('./rules-parser'); +import { parseWithImports } from './imports-parser'; import {parseExpression} from './parse-util'; +let parser = require('./rules-parser'); + var errors = { badIndex: "The index function must return a String or an array of Strings.", noPaths: "Must have at least one path expression.", @@ -87,7 +89,7 @@ var writeAliases = <{ [method: string]: ast.Exp }> { // json = bolt.generate(bolt-text) export function generate(symbols: string | ast.Symbols): Validator { if (typeof symbols === 'string') { - symbols = parser.parse(symbols); + symbols = parser.parse(parseWithImports(symbols)); } var gen = new Generator( symbols); return gen.generateRules(); @@ -246,20 +248,21 @@ export class Generator { // } // Key must derive from String getMapValidator(params: ast.Exp[]): Validator { + let lSymbols = this.symbols; let keyType = params[0]; let valueType = params[1]; - if (keyType.type !== 'type' || !this.symbols.isDerivedFrom(keyType, 'String')) { + if (keyType.type !== 'type' || !lSymbols.isDerivedFrom(keyType, 'String')) { throw new Error(errors.invalidMapKey + " (" + ast.decodeExpression(keyType) + " does not)"); } let validator = {}; let index = this.uniqueKey(); validator[index] = {}; - extendValidator(validator, this.ensureValidator(ast.typeType('Object'))); + extendValidator(validator, this.ensureValidator(ast.typeType('Object'), lSymbols)); // First validate the key (omit terminal String type validation). while (keyType.name !== 'String') { - let schema = this.symbols.schema[keyType.name]; + let schema = lSymbols.schema[keyType.name]; if (schema.methods['validate']) { let exp = this.partialEval(schema.methods['validate'].body, {'this': ast.literal(index)}); extendValidator( validator[index], {'.validate': [exp]}); @@ -267,7 +270,7 @@ export class Generator { keyType = schema.derivedFrom; } - extendValidator( validator[index], this.ensureValidator(valueType)); + extendValidator( validator[index], this.ensureValidator(valueType, lSymbols)); return validator; } @@ -284,29 +287,29 @@ export class Generator { } // Ensure we have a definition for a validator for the given schema. - ensureValidator(type: ast.ExpType): Validator { + ensureValidator(type: ast.ExpType, lSymbols: ast.Symbols): Validator { var key = ast.decodeExpression(type); if (!this.validators[key]) { this.validators[key] = {'.validate': ast.literal('***TYPE RECURSION***') }; let allowSave = this.allowUndefinedFunctions; this.allowUndefinedFunctions = true; - this.validators[key] = this.createValidator(type); + this.validators[key] = this.createValidator(type, lSymbols); this.allowUndefinedFunctions = allowSave; } return this.validators[key]; } - createValidator(type: ast.ExpType): Validator { + createValidator(type: ast.ExpType, lSymbols: ast.Symbols): Validator { switch (type.type) { case 'type': - return this.createValidatorFromSchemaName(( type).name); + return this.createValidatorFromSchemaNameNamespaced(( type), lSymbols); case 'union': let union = {}; ( type).types.forEach((typePart: ast.ExpType) => { // Make a copy - var singleType = extendValidator({}, this.ensureValidator(typePart)); + var singleType = extendValidator({}, this.ensureValidator(typePart, lSymbols)); mapValidator(singleType, ast.andArray); extendValidator(union, singleType); }); @@ -347,7 +350,7 @@ export class Generator { // Expand generics and generate validator from schema. schema = this.replaceGenericsInSchema(schema, bindings); - return this.createValidatorFromSchema(schema); + return this.createValidatorFromSchema(schema, this.symbols); } replaceGenericsInSchema(schema: ast.Schema, bindings: ast.TypeParams): ast.Schema { @@ -414,8 +417,48 @@ export class Generator { return expandedMethod; } - createValidatorFromSchemaName(schemaName: string): Validator { - var schema = this.symbols.schema[schemaName]; + createValidatorFromSchemaNameNamespaced(schemaName: ast.ExpSimpleType, lSymbols: ast.Symbols): Validator { + var schema = lSymbols.schema[schemaName.name]; + let ref = this; + + // imp.alias is the alias to apply to the imports + // schemaName.namespace is the alias applied to the type + if (!schema) { + lSymbols.imports + .sort(function(x, y) { + return x.alias ? 1 : 0; + }).map( imp => { + if (imp.alias === schemaName.namespace || (!imp.alias && !schemaName.namespace)) { // namespace match + if (imp.identifiers.indexOf(schemaName.name) >= 0 || imp.identifiers.length === 0) { // identifier list match + schema = imp.symbols.schema[schemaName.name]; + if (schema) { + let derivedValidator = ref.createValidatorFromSchema(schema, imp.symbols); + return extendValidator(derivedValidator, this.ensureValidator(schema.derivedFrom, imp.symbols)); + } + } + } + }); + } + + // Fall back to generics if it get's to that. + // Question: Will this break the generic test below? + if (!schema) { + if (builtinSchemaNames.indexOf(schemaName.name) >= 0 ) { + schema = this.symbols.schema[schemaName.name]; + } + } + if (!schema) { + throw new Error(errors.noSuchType + schemaName.name); + } + + if (ast.Schema.isGeneric(schema)) { + throw new Error(errors.noSuchType + schemaName + " used as non-generic type."); + } + + return this.createValidatorFromSchema(schema, lSymbols); + } + createValidatorFromSchemaName(schemaName: string, lSymbols: ast.Symbols): Validator { + var schema = lSymbols.schema[schemaName]; if (!schema) { throw new Error(errors.noSuchType + schemaName); @@ -425,14 +468,14 @@ export class Generator { throw new Error(errors.noSuchType + schemaName + " used as non-generic type."); } - return this.createValidatorFromSchema(schema); + return this.createValidatorFromSchema(schema, lSymbols); } - createValidatorFromSchema(schema: ast.Schema): Validator { + createValidatorFromSchema(schema: ast.Schema, lSymbols: ast.Symbols): Validator { var hasProps = Object.keys(schema.properties).length > 0 && !this.isCollectionSchema(schema); - if (hasProps && !this.symbols.isDerivedFrom(schema.derivedFrom, 'Object')) { + if (hasProps && !lSymbols.isDerivedFrom(schema.derivedFrom, 'Object')) { this.fatal(errors.nonObject + " (is " + ast.decodeExpression(schema.derivedFrom) + ")"); return {}; } @@ -441,7 +484,7 @@ export class Generator { if (!(schema.derivedFrom.type === 'type' && ( schema.derivedFrom).name === 'Any')) { - extendValidator(validator, this.ensureValidator(schema.derivedFrom)); + extendValidator(validator, this.ensureValidator(schema.derivedFrom, lSymbols)); } let requiredProperties = []; @@ -464,7 +507,7 @@ export class Generator { if (propName[0] !== '$' && !this.isNullableType(propType)) { requiredProperties.push(propName); } - extendValidator( validator[propName], this.ensureValidator(propType)); + extendValidator( validator[propName], this.ensureValidator(propType, lSymbols)); }); if (wildProperties > 1 || wildProperties === 1 && requiredProperties.length > 0) { @@ -500,8 +543,9 @@ export class Generator { var i: number; var location = util.ensureObjectPath(this.rules, path.template.getLabels()); var exp: ast.ExpValue; + var lSymbols = this.symbols; - extendValidator(location, this.ensureValidator(path.isType)); + extendValidator(location, this.ensureValidator(path.isType, lSymbols)); location['.scope'] = path.template.getScope(); this.extendValidationMethods(location, path.methods); diff --git a/src/rules-parser.pegjs b/src/rules-parser.pegjs index aa806bb..8a0a7d8 100644 --- a/src/rules-parser.pegjs +++ b/src/rules-parser.pegjs @@ -84,7 +84,29 @@ start = _ Statements _ { Statements = rules:(Statement _)* -Statement = f:Function / p:Path / s:Schema +Statement = i:Import / f:Function / p:Path / s:Schema + +AliasName "aliasname definition" = "as" _ name:Identifier { + return name; +} + +GlobalImport "global import" = "*" { + return []; +} + +SpecificImports "specific import" = "{" _ identifiers:IdentifierList _ "}" +{ + return identifiers; +} + + +Import "import definition" = "import" _ identifiers:(GlobalImport / SpecificImports)? _ alias:(AliasName)? _"from" _"'" _ filePath:FilePath _ "'" { + symbols.registerImport(identifiers, alias, filePath); +} + +FilePath "file path" = start:[a-zA-Z\.] rest:([a-zA-Z_\.\\\/0-9\- ])+ { + return start + rest.join(""); +} Function "function definition" = func:FunctionStart body:FunctionBody? { if (func.name === null) { @@ -287,19 +309,32 @@ TypeExpression = head:SingleType tail:(_ "|" _ type:SingleType { return type; } return ast.unionType(tail); } +NamespacedIdentifier = namespace:(Identifier ".")? id:Identifier { + if(namespace){ + return { + namespace: namespace[0], + id: id + } + } else { + return { + id: id + } + } +} + // Type, Type[], or Type // where Type[] === Map -SingleType = type:Identifier opt:("\[\]" {return {isMap: true}; } +SingleType = type:NamespacedIdentifier opt:("\[\]" {return {isMap: true}; } / "<" _ types:TypeList ">" {return {types: types};})? _ { - type = ensureUpperCase(type, "Type names"); + ensureUpperCase(type.id, "Type names"); if (!opt) { - return ast.typeType(type); + return ast.typeTypeNamespaced(type.id, type.namespace); } if (opt.isMap) { - return ast.genericType('Map', [ast.typeType('String'), - ast.typeType(type)]); + return ast.genericType('Map', [ast.typeType('String', null), + ast.typeTypeNamespaced(type.id, type.namespace)]); } - return ast.genericType(type, opt.types); + return ast.genericType(type.id, opt.types); } TypeList = head:TypeExpression tail:(_ "," _ type:TypeExpression { return type; })* _ { diff --git a/src/test/ast-test.ts b/src/test/ast-test.ts index 13ea702..11035f6 100644 --- a/src/test/ast-test.ts +++ b/src/test/ast-test.ts @@ -15,11 +15,12 @@ */ import {assert} from 'chai'; import * as helper from './test-helper'; - import * as bolt from '../bolt'; -let parse = bolt.parse; import * as ast from '../ast'; +let rulesParser = require('../rules-parser'); +let parse = rulesParser.parse; + suite("Abstract Syntax Tree (AST)", function() { suite("Left Associative Operators (AND OR)", function() { var t = ast.boolean(true); diff --git a/src/test/generator-test.ts b/src/test/generator-test.ts index fbafd5b..e42127d 100644 --- a/src/test/generator-test.ts +++ b/src/test/generator-test.ts @@ -183,7 +183,7 @@ suite("Rules Generator Tests", function() { helper.dataDrivenTest(tests, function(data, expect) { var symbols = parse("path / {}"); var gen = new bolt.Generator(symbols); - gen.ensureValidator(ast.typeType(data)); + gen.ensureValidator(ast.typeType(data), symbols); var terms = gen.validators[data]['.validate']; var result = bolt.decodeExpression(ast.andArray(terms)); diff --git a/src/test/parser-test.ts b/src/test/parser-test.ts index 7a72d71..40233f2 100644 --- a/src/test/parser-test.ts +++ b/src/test/parser-test.ts @@ -33,6 +33,61 @@ suite("Rules Parser Tests", function() { assert.ok(result instanceof ast.Symbols); }); + suite("Imports", function(){ + var tests = [ + { data: "import * from 'foo'", + expect: { + filename: ast.string('foo'), + alias: ast.nullType(), + identifiers: ast.array([]) + } + }, + { data: "import * from '../../foo/bar'", + expect: { + filename: ast.string('../../foo/bar'), + alias: ast.nullType(), + identifiers: ast.array([]) + } + }, + { data: "import {SomeType} from './foo/bar'", + expect: { + filename: ast.string('./foo/bar'), + alias: ast.nullType(), + identifiers: ast.array(['SomeType']) + } + }, + { data: "import * as lol from './foo/bar'", + expect: { + filename: ast.string('./foo/bar'), + alias: ast.string('lol'), + identifiers: ast.array([]) + } + }, + { data: "import {SomeType} as lol from './foo-bar'", + expect: { + filename: ast.string('./foo-bar'), + alias: ast.string('lol'), + identifiers: ast.array(['SomeType']) + } + } + ]; + helper.dataDrivenTest(tests, function(data, expect) { + var result = parse(data); + assert.deepEqual(result.imports[0].filename, expect.filename.value); + assert.isArray(result.imports[0].identifiers); + assert.equal(result.imports[0].identifiers.length, expect.identifiers.value.length); + + result.imports[0].identifiers.map( (x: any, index: number) => { + assert.deepEqual(expect.identifiers.value[index], x); + }); + if (result.imports[0].alias) { + assert.deepEqual(result.imports[0].alias, expect.alias.value); + } else { + assert.isUndefined(expect.alias.value); + } + }); + }); + suite("Function Samples", function() { var tests: helper.ObjectSpec[] = [ { data: "function f() { return true; }", diff --git a/src/util.ts b/src/util.ts index 3296f17..ac9fe5d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -88,7 +88,7 @@ export function lift(fn: (...args: any[]) => T) // fn(U, V, ...): Promise => fn(U | Promise, V | Promise, ...): Promise export let liftArgs: (fn: (...args: any[]) => Promise) => - ((...args: any[]) => Promise) = lift; + ((...args: any[]) => Promise) = lift; export let getProp = lift((obj, prop) => obj[prop]); diff --git a/tsconfig.json b/tsconfig.json index 6fb855f..2fc5401 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "rootDir": "src", "module": "commonjs", "noImplicitAny": true, - "strictNullChecks": true, + "strictNullChecks": false, "preserveConstEnums": true, "inlineSourceMap": true, "noEmitOnError": true