diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -2,3 +2,4 @@ syntax: glob
.gradle/
build/
node_modules/
+src/typings/
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -1,1 +1,2 @@
9b7927c5bafc1c80e589d9feb807e428075ef513 v1.1.1
+43a2828f8abeb9f2f9bfaf9e6d0e0b370c8a6456 v1.2.0-rc
diff --git a/.project b/.project
new file mode 100644
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ core
+ Project core created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
--- /dev/null
+++ b/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,15 @@
+{
+ "java.configuration.updateBuildConfiguration": "disabled",
+ "tslint.enable": true,
+ "search.exclude": {
+ "**/node_modules": true,
+ "**/bower_components": true,
+ "/build": true
+ },
+ "files.watcherExclude": {
+ "**/.git/objects/**": true,
+ "**/.git/subtree-cache/**": true,
+ "**/node_modules/**": true,
+ "/build": true
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -2,15 +2,41 @@ if (release != 'rtm') {
version += "-$release"
}
-println "version: $version"
+if(!npmName)
+ npmName = name;
+
+if(!["amd", "cjs"].contains(platform))
+ throw new Exception("Invalid platform specified: $platform");
+
+def moduleTypes = [
+ "amd": "amd",
+ "cjs": "commonjs"
+]
+
+ext.packageName="$npmScope/$npmName-$platform";
-def distDir = "$buildDir/dist"
-def testDir = "$buildDir/test"
+def srcDir = "$projectDir/src"
+def typingsDir = "$srcDir/typings"
+def distDir = "$buildDir/dist/$platform"
+def testDir = "$buildDir/test/$platform"
+def moduleType = moduleTypes[platform]
+
+def sourceSets = ["main", "amd", "cjs", "test"];
+
+task printVersion {
+ doLast {
+ println "version: $version"
+ println "packageName: $packageName"
+ println "platform: $platform"
+ println "module: $moduleType"
+ }
+}
task clean {
doLast {
delete buildDir
- delete 'node_modules/@implab'
+ delete "node_modules/$packageName"
+ delete typingsDir
}
}
@@ -30,65 +56,79 @@ task _npmInstall() {
}
}
-task _legacyJs(type:Copy) {
- from 'src/js/'
- into distDir
+sourceSets.each {
+ def setName = it.capitalize();
+
+ def destDir = "$buildDir/compile/$it"
+ def declDir = "$typingsDir/$it"
+ def setDir = "$projectDir/src/$it"
+
+ task "_copyJs$setName"(type:Copy) {
+ from "$setDir/js"
+ into distDir
+ }
+
+ task "_compileTs$setName"(dependsOn: _npmInstall, type:Exec) {
+ inputs.dir("$setDir/ts")
+ inputs.file("$srcDir/tsconfig.json")
+ inputs.file("$setDir/tsconfig.json")
+ outputs.dir(destDir)
+ outputs.dir(declDir)
+
+ commandLine 'node_modules/.bin/tsc',
+ '-p', "$setDir/tsconfig.json",
+ '-m', moduleType,
+ '--outDir', destDir,
+ '--declarationDir', declDir
+ }
+
+ task "_buildTs$setName"(dependsOn: "_compileTs$setName", type:Copy) {
+ from tasks.getByPath("_compileTs$setName");
+ into distDir
+ }
}
-task _buildTs(dependsOn: _npmInstall, type:Exec) {
- inputs.dir('src/ts')
- inputs.file('tsc.json')
- outputs.dir(distDir)
+_compileTsAmd {
+ dependsOn _buildTsMain
+}
- commandLine 'node_modules/.bin/tsc', '-p', 'tsc.json'
+_buildTsTest {
+ into testDir
+}
+
+_copyJsTest {
+ into testDir
}
task _packageMeta(type: Copy) {
inputs.property("version", version)
from('.') {
- include 'package.json', '.npmignore', 'readme.md', 'license', 'history.md'
+ include '.npmignore', 'readme.md', 'license', 'history.md'
+ }
+ from("$srcDir/package.template.json") {
+ expand project.properties
+ rename { "package.json" }
}
into distDir
- doLast {
- exec {
- workingDir distDir
- commandLine 'npm', 'version', version
- }
- }
}
-task build(dependsOn: [_npmInstall, _buildTs, _legacyJs, _packageMeta]) {
+task build(dependsOn: [_copyJsMain, _copyJsAmd, _npmInstall, _buildTsMain, _buildTsAmd, _packageMeta]) {
}
-task _localInstall(dependsOn: build, type: Exec) {
- inputs.file("$distDir/package.json")
- outputs.upToDateWhen {
- new File("$projectDir/node_modules/@implab/core").exists()
- }
-
- commandLine 'npm', 'install', '--no-save', '--force', distDir
+_compileTsTest {
+ dependsOn build
}
-task copyJsTests(type: Copy) {
- from 'test/js'
- into testDir
+task buildTests(dependsOn: [_copyJsTest, _buildTsTest]) {
}
-task buildTests(dependsOn: _localInstall, type: Exec) {
- inputs.dir('test/ts')
- inputs.file('tsc.test.json')
- outputs.dir(testDir)
-
- commandLine 'node_modules/.bin/tsc', '-p', 'tsc.test.json'
-}
-
-task test(dependsOn: [copyJsTests, buildTests], type: Exec) {
- commandLine 'node', 'run-amd-tests.js'
+task test(dependsOn: buildTests, type: Exec) {
+ commandLine 'node', "$testDir/run-amd-tests.js"
}
task pack(dependsOn: build, type: Exec) {
- workingDir = distDir
+ workingDir distDir
commandLine 'npm', 'pack'
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,9 @@
-version=1.1.1
-release=rtm
\ No newline at end of file
+version=1.2.0
+release=rc
+author=Implab team
+platform=amd
+description=Dependency injection, logging, simple and fast text template engine
+license=BSD-2-Clause
+repository=https://bitbucket.org/implab/implabjs
+npmScope=@implab
+npmName=core
\ No newline at end of file
diff --git a/license b/license
--- a/license
+++ b/license
@@ -1,4 +1,4 @@
-Copyright 2017-2018 Implab team
+Copyright 2017-2019 Implab team
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/package-lock.json b/package-lock.json
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,9 +5,15 @@
"requires": true,
"dependencies": {
"@types/node": {
- "version": "10.5.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.1.tgz",
- "integrity": "sha512-AFLl1IALIuyt6oK4AYZsgWVJ/5rnyzQWud7IebaZWWV3YmgtPZkQmYio9R5Ze/2pdd7XfqF5bP+hWS11mAKoOQ==",
+ "version": "10.12.15",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz",
+ "integrity": "sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA==",
+ "dev": true
+ },
+ "@types/requirejs": {
+ "version": "2.1.31",
+ "resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.31.tgz",
+ "integrity": "sha512-b2soeyuU76rMbcRJ4e0hEl0tbMhFwZeTC0VZnfuWlfGlk6BwWNsev6kFu/twKABPX29wkX84wU2o+cEJoXsiTw==",
"dev": true
},
"@types/tape": {
@@ -16,7 +22,7 @@
"integrity": "sha512-xil0KO5wkPoixdBWGIGolPv9dekf6dVkjjJLAFYchfKcd4DICou67rgGCIO7wAh3i5Ff/6j9IDgZz+GU9cMaqQ==",
"dev": true,
"requires": {
- "@types/node": "10.5.1"
+ "@types/node": "*"
}
},
"balanced-match": {
@@ -31,7 +37,7 @@
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
- "balanced-match": "1.0.0",
+ "balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
@@ -48,35 +54,43 @@
"dev": true
},
"deep-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
- "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz",
+ "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=",
"dev": true
},
"define-properties": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
- "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
- "foreach": "2.0.5",
- "object-keys": "1.0.12"
+ "object-keys": "^1.0.12"
+ },
+ "dependencies": {
+ "object-keys": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+ "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
+ "dev": true
+ }
}
},
"defined": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
- "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz",
+ "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=",
"dev": true
},
"dojo": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.13.0.tgz",
- "integrity": "sha512-mGoGvsXAbPkUrBnxCoO7m6CFH8jvWq7rAL7fP7jrhJEOyswA/bZwWdXwEH0ovs68t8S0+xOpV/3V7addYbaiAA=="
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.14.2.tgz",
+ "integrity": "sha512-TI+Ytgfh/VfmHWERp45Jte6NFMdoJTPsvUP/uzJUvAXET8FP2h442LePWWJ/q/xZ4V0V8OtdJhx8It/GB+Zbxg==",
+ "dev": true
},
"duplexer": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
"dev": true
},
@@ -86,22 +100,22 @@
"integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
"dev": true,
"requires": {
- "es-to-primitive": "1.1.1",
- "function-bind": "1.1.1",
- "has": "1.0.3",
- "is-callable": "1.1.3",
- "is-regex": "1.0.4"
+ "es-to-primitive": "^1.1.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.1",
+ "is-callable": "^1.1.3",
+ "is-regex": "^1.0.4"
}
},
"es-to-primitive": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
- "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
"dev": true,
"requires": {
- "is-callable": "1.1.3",
- "is-date-object": "1.0.1",
- "is-symbol": "1.0.1"
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
}
},
"faucet": {
@@ -111,44 +125,26 @@
"dev": true,
"requires": {
"defined": "0.0.0",
- "duplexer": "0.1.1",
+ "duplexer": "~0.1.1",
"minimist": "0.0.5",
- "sprintf": "0.1.5",
- "tap-parser": "0.4.3",
- "tape": "2.3.3",
- "through2": "0.2.3"
+ "sprintf": "~0.1.3",
+ "tap-parser": "~0.4.0",
+ "tape": "~2.3.2",
+ "through2": "~0.2.3"
},
"dependencies": {
- "deep-equal": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz",
- "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=",
- "dev": true
- },
- "defined": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz",
- "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=",
- "dev": true
- },
- "minimist": {
- "version": "0.0.5",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
- "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=",
- "dev": true
- },
"tape": {
"version": "2.3.3",
- "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
+ "resolved": "http://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
"integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
"dev": true,
"requires": {
- "deep-equal": "0.1.2",
- "defined": "0.0.0",
- "inherits": "2.0.3",
- "jsonify": "0.0.0",
- "resumer": "0.0.0",
- "through": "2.3.8"
+ "deep-equal": "~0.1.0",
+ "defined": "~0.0.0",
+ "inherits": "~2.0.1",
+ "jsonify": "~0.0.0",
+ "resumer": "~0.0.0",
+ "through": "~2.3.4"
}
}
}
@@ -159,15 +155,9 @@
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"requires": {
- "is-callable": "1.1.3"
+ "is-callable": "^1.1.3"
}
},
- "foreach": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
- "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
- "dev": true
- },
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -181,17 +171,17 @@
"dev": true
},
"glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
}
},
"has": {
@@ -200,17 +190,23 @@
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
- "function-bind": "1.1.1"
+ "function-bind": "^1.1.1"
}
},
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
+ "once": "^1.3.0",
+ "wrappy": "1"
}
},
"inherits": {
@@ -220,9 +216,9 @@
"dev": true
},
"is-callable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
- "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
"dev": true
},
"is-date-object": {
@@ -237,14 +233,17 @@
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
- "has": "1.0.3"
+ "has": "^1.0.1"
}
},
"is-symbol": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
- "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
- "dev": true
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.0"
+ }
},
"isarray": {
"version": "0.0.1",
@@ -264,13 +263,13 @@
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
- "brace-expansion": "1.1.11"
+ "brace-expansion": "^1.1.7"
}
},
"minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "version": "0.0.5",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
+ "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=",
"dev": true
},
"object-inspect": {
@@ -280,9 +279,9 @@
"dev": true
},
"object-keys": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
- "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=",
"dev": true
},
"once": {
@@ -291,31 +290,31 @@
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
- "wrappy": "1.0.2"
+ "wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
- "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"readable-stream": {
"version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
"isarray": "0.0.1",
- "string_decoder": "0.10.31"
+ "string_decoder": "~0.10.x"
}
},
"requirejs": {
@@ -326,11 +325,11 @@
},
"resolve": {
"version": "1.7.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
+ "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
"dev": true,
"requires": {
- "path-parse": "1.0.5"
+ "path-parse": "^1.0.5"
}
},
"resumer": {
@@ -339,7 +338,7 @@
"integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
"dev": true,
"requires": {
- "through": "2.3.8"
+ "through": "~2.3.4"
}
},
"sprintf": {
@@ -354,14 +353,14 @@
"integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
"dev": true,
"requires": {
- "define-properties": "1.1.2",
- "es-abstract": "1.12.0",
- "function-bind": "1.1.1"
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.5.0",
+ "function-bind": "^1.0.2"
}
},
"string_decoder": {
"version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
},
@@ -371,8 +370,8 @@
"integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=",
"dev": true,
"requires": {
- "inherits": "2.0.3",
- "readable-stream": "1.1.14"
+ "inherits": "~2.0.1",
+ "readable-stream": "~1.1.11"
}
},
"tape": {
@@ -381,41 +380,67 @@
"integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==",
"dev": true,
"requires": {
- "deep-equal": "1.0.1",
- "defined": "1.0.0",
- "for-each": "0.3.3",
- "function-bind": "1.1.1",
- "glob": "7.1.2",
- "has": "1.0.3",
- "inherits": "2.0.3",
- "minimist": "1.2.0",
- "object-inspect": "1.6.0",
- "resolve": "1.7.1",
- "resumer": "0.0.0",
- "string.prototype.trim": "1.1.2",
- "through": "2.3.8"
+ "deep-equal": "~1.0.1",
+ "defined": "~1.0.0",
+ "for-each": "~0.3.3",
+ "function-bind": "~1.1.1",
+ "glob": "~7.1.2",
+ "has": "~1.0.3",
+ "inherits": "~2.0.3",
+ "minimist": "~1.2.0",
+ "object-inspect": "~1.6.0",
+ "resolve": "~1.7.1",
+ "resumer": "~0.0.0",
+ "string.prototype.trim": "~1.1.2",
+ "through": "~2.3.8"
+ },
+ "dependencies": {
+ "deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
+ "dev": true
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
}
},
"through": {
"version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"through2": {
"version": "0.2.3",
- "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
+ "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
"integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
"dev": true,
"requires": {
- "readable-stream": "1.1.14",
- "xtend": "2.1.2"
+ "readable-stream": "~1.1.9",
+ "xtend": "~2.1.1"
}
},
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+ "dev": true
+ },
"typescript": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
- "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
+ "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==",
"dev": true
},
"wrappy": {
@@ -430,15 +455,7 @@
"integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
"dev": true,
"requires": {
- "object-keys": "0.4.0"
- },
- "dependencies": {
- "object-keys": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
- "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=",
- "dev": true
- }
+ "object-keys": "~0.4.0"
}
}
}
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -16,15 +16,20 @@
"publishConfig": {
"access": "public"
},
- "dependencies": {
- "dojo": "^1.10.0"
+ "peerDependencies": {
+ "dojo": "^1.10.0",
+ "tslib": "latest"
},
"devDependencies": {
"typescript": "latest",
"tape": "latest",
"@types/tape": "latest",
+ "@types/requirejs": "latest",
+ "@types/node": "latest",
"requirejs": "latest",
- "faucet": "latest"
+ "faucet": "latest",
+ "dojo": "^1.10.0",
+ "tslib": "latest"
},
"types": "main.d.ts"
}
diff --git a/settings.gradle b/settings.gradle
--- a/settings.gradle
+++ b/settings.gradle
@@ -12,4 +12,4 @@
//include 'sub-project-name'
-rootProject.name = 'implab-core'
\ No newline at end of file
+rootProject.name = 'core'
\ No newline at end of file
diff --git a/src/js/Uri.js b/src/amd/js/Uri.js
rename from src/js/Uri.js
rename to src/amd/js/Uri.js
diff --git a/src/js/data/DataContext.js b/src/amd/js/data/DataContext.js
rename from src/js/data/DataContext.js
rename to src/amd/js/data/DataContext.js
diff --git a/src/js/data/MapSchema.js b/src/amd/js/data/MapSchema.js
rename from src/js/data/MapSchema.js
rename to src/amd/js/data/MapSchema.js
diff --git a/src/js/data/ObjectStore.js b/src/amd/js/data/ObjectStore.js
rename from src/js/data/ObjectStore.js
rename to src/amd/js/data/ObjectStore.js
diff --git a/src/js/data/RestStore.js b/src/amd/js/data/RestStore.js
rename from src/js/data/RestStore.js
rename to src/amd/js/data/RestStore.js
diff --git a/src/js/data/StatefullStoreAdapter.js b/src/amd/js/data/StatefullStoreAdapter.js
rename from src/js/data/StatefullStoreAdapter.js
rename to src/amd/js/data/StatefullStoreAdapter.js
diff --git a/src/js/data/StoreAdapter.js b/src/amd/js/data/StoreAdapter.js
rename from src/js/data/StoreAdapter.js
rename to src/amd/js/data/StoreAdapter.js
diff --git a/src/js/data/_ModelBase.js b/src/amd/js/data/_ModelBase.js
rename from src/js/data/_ModelBase.js
rename to src/amd/js/data/_ModelBase.js
diff --git a/src/js/data/_StatefulModelMixin.js b/src/amd/js/data/_StatefulModelMixin.js
rename from src/js/data/_StatefulModelMixin.js
rename to src/amd/js/data/_StatefulModelMixin.js
diff --git a/src/js/data/declare-model.js b/src/amd/js/data/declare-model.js
rename from src/js/data/declare-model.js
rename to src/amd/js/data/declare-model.js
diff --git a/src/js/declare/_load.js b/src/amd/js/declare/_load.js
rename from src/js/declare/_load.js
rename to src/amd/js/declare/_load.js
diff --git a/src/js/declare/override.js b/src/amd/js/declare/override.js
rename from src/js/declare/override.js
rename to src/amd/js/declare/override.js
diff --git a/src/js/log/trace.js b/src/amd/js/log/trace.js
rename from src/js/log/trace.js
rename to src/amd/js/log/trace.js
diff --git a/src/js/messaging/Client.js b/src/amd/js/messaging/Client.js
rename from src/js/messaging/Client.js
rename to src/amd/js/messaging/Client.js
diff --git a/src/js/messaging/Destination.js b/src/amd/js/messaging/Destination.js
rename from src/js/messaging/Destination.js
rename to src/amd/js/messaging/Destination.js
diff --git a/src/js/messaging/Listener.js b/src/amd/js/messaging/Listener.js
rename from src/js/messaging/Listener.js
rename to src/amd/js/messaging/Listener.js
diff --git a/src/js/messaging/Session.js b/src/amd/js/messaging/Session.js
rename from src/js/messaging/Session.js
rename to src/amd/js/messaging/Session.js
diff --git a/src/amd/ts/di/RequireJsHelper.ts b/src/amd/ts/di/RequireJsHelper.ts
new file mode 100644
--- /dev/null
+++ b/src/amd/ts/di/RequireJsHelper.ts
@@ -0,0 +1,41 @@
+import { Uuid } from "../Uuid";
+import { argumentNotEmptyString, argumentNotNull } from "../safe";
+import { TraceSource } from "../log/TraceSource";
+import m = require("module");
+
+const trace = TraceSource.get(m.id);
+
+export async function createContextRequire(moduleName: string): Promise {
+ argumentNotEmptyString(moduleName, "moduleName");
+
+ const parts = moduleName.split("/");
+ if (parts[0] === ".")
+ throw new Error("An absolute module path is required");
+
+ if (parts.length > 1)
+ parts.splice(-1, 1, Uuid());
+ else
+ parts.push(Uuid());
+
+ const shim = parts.join("/");
+
+ trace.debug(`define shim ${shim}`);
+
+ return new Promise(cb => {
+ define(shim, ["require"], r => {
+ trace.debug("shim resolved");
+ return r;
+ });
+ require([shim], cb);
+ });
+}
+
+export function makeResolver(req: Require) {
+ argumentNotNull(req, "req");
+
+ return (name: string) => {
+ return new Promise((cb, eb) => {
+ req([name], cb, eb);
+ });
+ };
+}
diff --git a/src/amd/ts/text/format-compile.ts b/src/amd/ts/text/format-compile.ts
new file mode 100644
--- /dev/null
+++ b/src/amd/ts/text/format-compile.ts
@@ -0,0 +1,8 @@
+import * as module from "module";
+import { TraceSource } from "../log/TraceSource";
+
+const logger = TraceSource.get(module.id);
+
+logger.warn("The module is deprecated, use StringFormat.compile() method directly");
+
+export { compile } from "./StringFormat";
diff --git a/src/amd/ts/text/format.ts b/src/amd/ts/text/format.ts
new file mode 100644
--- /dev/null
+++ b/src/amd/ts/text/format.ts
@@ -0,0 +1,47 @@
+import { format as dojoFormatNumber } from "dojo/number";
+import { format as dojoFormatDate } from "dojo/date/locale";
+import { Formatter } from "./StringFormat";
+
+import { isNumber } from "../safe";
+
+interface NumberFormatOptions {
+ round?: number;
+ pattern?: string;
+}
+
+function convertNumber(value: any, pattern: string) {
+ if (isNumber(value)) {
+ const nopt = {} as NumberFormatOptions;
+ if (pattern.indexOf("!") === 0) {
+ nopt.round = -1;
+ pattern = pattern.substr(1);
+ }
+ nopt.pattern = pattern;
+
+ return dojoFormatNumber(value, nopt);
+ }
+}
+
+function convertDate(value: any, pattern: string) {
+ if (value instanceof Date) {
+ const m = pattern.match(/^(\w+)-(\w+)$/);
+ if (m)
+ return dojoFormatDate(value, {
+ selector: m[2],
+ formatLength: m[1]
+ });
+ else if (pattern === "iso")
+ return value.toISOString();
+ else
+ return dojoFormatDate(value, {
+ selector: "date",
+ datePattern: pattern
+ });
+ }
+}
+
+const _formatter = new Formatter([convertNumber, convertDate]);
+
+export = function format(msg: string, ...args: any[]) {
+ return _formatter.format.apply(msg, ...args);
+};
diff --git a/src/amd/ts/text/template-compile.ts b/src/amd/ts/text/template-compile.ts
new file mode 100644
--- /dev/null
+++ b/src/amd/ts/text/template-compile.ts
@@ -0,0 +1,49 @@
+import request = require("dojo/request");
+import m = require("module");
+import { TraceSource } from "../log/TraceSource";
+import { TemplateCompiler } from "./TemplateCompiler";
+import { TemplateParser } from "./TemplateParser";
+import { isNullOrEmptyString } from "../safe";
+import { MapOf } from "../interfaces";
+
+type TemplateFn = (obj: object) => string;
+
+const trace = TraceSource.get(m.id);
+
+function compile(str: string) {
+ if (isNullOrEmptyString(str))
+ return () => "";
+
+ const parser = new TemplateParser(str);
+ const compiler = new TemplateCompiler();
+
+ return compiler.compile(parser);
+}
+
+const cache: MapOf = {};
+
+interface OnLoadFn {
+ (res: T): void;
+ error(e: any): void;
+}
+
+compile.load = (id: string, require: Require, callback: OnLoadFn) => {
+ const url = require.toUrl(id);
+ if (url in cache) {
+ trace.debug("{0} -> {1}: cached", id, url);
+ callback(cache[url]);
+ } else {
+ trace.debug("{0} -> {1}: load", id, url);
+ request(url).then(compile).then((tc: TemplateFn) => {
+ trace.debug("{0}: compiled", url);
+ callback(cache[url] = tc);
+ }, (err: any) => {
+ callback.error({
+ inner: err,
+ src: "@implab/core/text/template-compile"
+ });
+ });
+ }
+};
+
+export = compile;
diff --git a/src/amd/tsconfig.json b/src/amd/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/src/amd/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "types": [
+ "requirejs"
+ ],
+ "rootDir": "ts",
+ "rootDirs": [
+ "ts",
+ "../typings/main"
+ ]
+ },
+ "include": [
+ "ts/**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src/cjs/ts/di/CommonJsHelper.ts b/src/cjs/ts/di/CommonJsHelper.ts
new file mode 100644
--- /dev/null
+++ b/src/cjs/ts/di/CommonJsHelper.ts
@@ -0,0 +1,3 @@
+export function createContextResolver(moduleName: string) {
+ return (m: string) => { };
+}
diff --git a/src/cjs/tsconfig.json b/src/cjs/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/src/cjs/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "types": [
+ "@types/node"
+ ]
+ },
+ "include": [
+ "ts/**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src/js/Deferred.js b/src/js/Deferred.js
deleted file mode 100644
--- a/src/js/Deferred.js
+++ /dev/null
@@ -1,3 +0,0 @@
-define(["dojo/Deferred"], function(Deferred) {
- return Deferred;
-});
\ No newline at end of file
diff --git a/src/js/declare.js b/src/js/declare.js
deleted file mode 100644
--- a/src/js/declare.js
+++ /dev/null
@@ -1,6 +0,0 @@
-define([
- './declare/_load!'
-], function(declare) {
- 'use strict';
- return declare;
-});
\ No newline at end of file
diff --git a/src/js/di/ActivationContext.js b/src/js/di/ActivationContext.js
deleted file mode 100644
--- a/src/js/di/ActivationContext.js
+++ /dev/null
@@ -1,138 +0,0 @@
-define([
- "../declare",
- "../safe",
- "./Descriptor",
- "./ValueDescriptor",
- "../log/trace!"
-], function (declare, safe, Descriptor, Value, trace) {
- var Context = declare(null, {
-
- _cache: null,
-
- _services: null,
-
- _stack: null,
-
- _visited: null,
-
- container: null,
-
- _trace: true,
-
- constructor: function (container, services, cache, visited) {
- safe.argumentNotNull(container, "container");
- safe.argumentNotNull(services, "services");
-
- this._visited = visited || {};
- this._stack = [];
- this._cache = cache || {};
- this._services = services;
- this.container = container;
- },
-
- getService: function (name, def) {
- var d = this._services[name];
-
- if (!d)
- if (arguments.length > 1)
- return def;
- else
- throw new Error("Service '" + name + "' not found");
-
- return d.activate(this, name);
- },
-
- /**
- * registers services local to the the activation context
- *
- * @name{string} the name of the service
- * @service{string} the service descriptor to register
- */
- register: function (name, service) {
- safe.argumentNotEmptyString(name, "name");
-
- if (!(service instanceof Descriptor))
- service = new Value(service, true);
- this._services[name] = service;
- },
-
- clone: function () {
- return new Context(
- this.container,
- Object.create(this._services),
- this._cache,
- this._visited
- );
-
- },
-
- has: function (id) {
- return id in this._cache;
- },
-
- get: function (id) {
- return this._cache[id];
- },
-
- store: function (id, value) {
- return (this._cache[id] = value);
- },
-
- parse: function (data, name) {
- var me = this;
- if (safe.isPrimitive(data))
- return data;
-
- if (data instanceof Descriptor) {
- return data.activate(this, name);
- } else if (data instanceof Array) {
- me.enter(name);
- var v = data.map(function (x, i) {
- return me.parse(x, "." + i);
- });
- me.leave();
- return v;
- } else {
- me.enter(name);
- var result = {};
- for (var p in data)
- result[p] = me.parse(data[p], "." + p);
- me.leave();
- return result;
- }
- },
-
- visit: function (id) {
- var count = this._visited[id] || 0;
- this._visited[id] = count + 1;
- return count;
- },
-
- getStack: function () {
- return this._stack.slice().reverse();
- },
-
- enter: function (name, d, localize) {
- if (this._trace)
- trace.log("enter " + name + " " + (d || "") +
- (localize ? " localize" : ""));
- this._stack.push({
- name: name,
- service: d,
- scope: this._services
- });
- if (localize)
- this._services = Object.create(this._services);
- },
-
- leave: function () {
- var ctx = this._stack.pop();
- this._services = ctx.scope;
-
- if (this._trace)
- trace.log("leave " + ctx.name + " " + (ctx.service || ""));
- }
- });
-
- return Context;
-});
\ No newline at end of file
diff --git a/src/js/di/ActivationError.js b/src/js/di/ActivationError.js
deleted file mode 100644
--- a/src/js/di/ActivationError.js
+++ /dev/null
@@ -1,39 +0,0 @@
-define([
- "../declare"
-], function (declare) {
- return declare(null, {
- activationStack: null,
-
- service: null,
-
- innerException: null,
-
- message: null,
-
- constructor: function (service, activationStack, innerException) {
- this.message = "Failed to activate the service";
- this.activationStack = activationStack;
- this.service = service;
- this.innerException = innerException;
- },
-
- toString: function () {
- var parts = [this.message];
- if (this.service)
- parts.push("when activating: " + this.service.toString());
-
- if (this.innerException)
- parts.push("caused by: " + this.innerException.toString());
-
- if (this.activationStack) {
- parts.push("at");
- this.activationStack.forEach(function (x) {
- parts.push(" " + x.name + " " +
- (x.service ? x.service.toString() : ""));
- });
- }
-
- return parts.join("\n");
- }
- });
-});
\ No newline at end of file
diff --git a/src/js/di/Container.js b/src/js/di/Container.js
deleted file mode 100644
--- a/src/js/di/Container.js
+++ /dev/null
@@ -1,299 +0,0 @@
-define([
- "../declare",
- "../safe",
- "../Uuid",
- "../Deferred",
- "./ActivationContext",
- "./Descriptor",
- "./ValueDescriptor",
- "./ReferenceDescriptor",
- "./ServiceDescriptor",
- "./ActivationError"
-], function (
- declare,
- safe,
- Uuid,
- Deferred,
- ActivationContext,
- Descriptor,
- Value,
- Reference,
- Service,
- ActivationError) {
- var Container = declare(null, {
- _services: null,
- _cache: null,
- _cleanup: null,
- _root: null,
- _parent: null,
-
- constructor: function (parent) {
- this._parent = parent;
- this._services = parent ? Object.create(parent._services) : {};
- this._cache = {};
- this._cleanup = [];
- this._root = parent ? parent.getRootContainer() : this;
- this._services.container = new Value(this, true);
- },
-
- getRootContainer: function () {
- return this._root;
- },
-
- getParent: function () {
- return this._parent;
- },
-
- /**
- *
- */
- getService: function (name, def) {
- var d = this._services[name];
- if (!d)
- if (arguments.length > 1)
- return def;
- else
- throw new Error("Service '" + name + "' isn't found");
- if (d.isInstanceCreated())
- return d.getInstance();
-
- var context = new ActivationContext(this, this._services);
-
- try {
- return d.activate(context, name);
- } catch (error) {
- throw new ActivationError(name, context.getStack(), error);
- }
- },
-
- register: function (name, service) {
- if (arguments.length == 1) {
- var data = name;
- for (name in data)
- this.register(name, data[name]);
- } else {
- if (!(service instanceof Descriptor))
- service = new Value(service, true);
- this._services[name] = service;
- }
- return this;
- },
-
- onDispose: function (callback) {
- if (!(callback instanceof Function))
- throw new Error("The callback must be a function");
- this._cleanup.push(callback);
- },
-
- dispose: function () {
- if (this._cleanup) {
- for (var i = 0; i < this._cleanup.length; i++)
- this._cleanup[i].call(null);
- this._cleanup = null;
- }
- },
-
- /**
- * @param{String|Object} config
- * The configuration of the contaier. Can be either a string or an object,
- * if the configuration is an object it's treated as a collection of
- * services which will be registed in the contaier.
- *
- * @param{Function} opts.contextRequire
- * The function which will be used to load a configuration or types for services.
- *
- */
- configure: function (config, opts) {
- var p, me = this,
- contextRequire = (opts && opts.contextRequire);
-
- if (typeof (config) === "string") {
- p = new Deferred();
- if (!contextRequire) {
- var shim = [config, Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
- define(shim, ["require", config], function (ctx, data) {
- p.resolve([data, {
- contextRequire: ctx
- }]);
- });
- require([shim]);
- } else {
- // TODO how to get correct contextRequire for the relative config module?
- contextRequire([config], function (data) {
- p.resolve([data, {
- contextRequire: contextRequire
- }]);
- });
- }
-
- return p.then(function (args) {
- return me._configure.apply(me, args);
- });
- } else {
- return me._configure(config, opts);
- }
- },
-
- createChildContainer: function () {
- return new Container(this);
- },
-
- has: function (id) {
- return id in this._cache;
- },
-
- get: function (id) {
- return this._cache[id];
- },
-
- store: function (id, value) {
- return (this._cache[id] = value);
- },
-
- _configure: function (data, opts) {
- var typemap = {},
- d = new Deferred(),
- me = this,
- p,
- contextRequire = (opts && opts.contextRequire) || require;
-
- var services = {};
-
- for (p in data) {
- var service = me._parse(data[p], typemap);
- if (!(service instanceof Descriptor))
- service = new Value(service, false);
- services[p] = service;
- }
-
- me.register(services);
-
- var names = [];
-
- for (p in typemap)
- names.push(p);
-
- if (names.length) {
- contextRequire(names, function () {
- for (var i = 0; i < names.length; i++)
- typemap[names[i]] = arguments[i];
- d.resolve(me);
- });
- } else {
- d.resolve(me);
- }
- return d.promise;
- },
-
- _parse: function (data, typemap) {
- if (safe.isPrimitive(data) || data instanceof Descriptor)
- return data;
- if (data.$dependency)
- return new Reference(
- data.$dependency,
- data.lazy,
- data.optional,
- data["default"],
- data.services && this._parseObject(data.services, typemap));
- if (data.$value) {
- var raw = !data.parse;
- return new Value(raw ? data.$value : this._parse(
- data.$value,
- typemap), raw);
- }
- if (data.$type || data.$factory)
- return this._parseService(data, typemap);
- if (data instanceof Array)
- return this._parseArray(data, typemap);
-
- return this._parseObject(data, typemap);
- },
-
- _parseService: function (data, typemap) {
- var me = this,
- opts = {
- owner: this
- };
- if (data.$type) {
-
- opts.type = data.$type;
-
- if (typeof (data.$type) === "string") {
- typemap[data.$type] = null;
- opts.typeMap = typemap;
- }
- }
-
- if (data.$factory)
- opts.factory = data.$factory;
-
- if (data.services)
- opts.services = me._parseObject(data.services, typemap);
- if (data.inject)
- opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
- return me._parseObject(x, typemap);
- }) : me._parseObject(data.inject, typemap);
- if (data.params)
- opts.params = me._parse(data.params, typemap);
-
- if (data.activation) {
- if (typeof (data.activation) === "string") {
- switch (data.activation.toLowerCase()) {
- case "singleton":
- opts.activation = Service.SINGLETON;
- break;
- case "container":
- opts.activation = Service.CONTAINER;
- break;
- case "hierarchy":
- opts.activation = Service.HIERARCHY;
- break;
- case "context":
- opts.activation = Service.CONTEXT;
- break;
- case "call":
- opts.activation = Service.CALL;
- break;
- default:
- throw new Error("Unknown activation type: " +
- data.activation);
- }
- } else {
- opts.activation = Number(data.activation);
- }
- }
-
- if (data.cleanup)
- opts.cleanup = data.cleanup;
-
- return new Service(opts);
- },
-
- _parseObject: function (data, typemap) {
- if (data.constructor &&
- data.constructor.prototype !== Object.prototype)
- return new Value(data, true);
-
- var o = {};
-
- for (var p in data)
- o[p] = this._parse(data[p], typemap);
-
- return o;
- },
-
- _parseArray: function (data, typemap) {
- if (data.constructor &&
- data.constructor.prototype !== Array.prototype)
- return new Value(data, true);
-
- var me = this;
- return data.map(function (x) {
- return me._parse(x, typemap);
- });
- }
-
- });
-
- return Container;
-});
\ No newline at end of file
diff --git a/src/js/di/Descriptor.js b/src/js/di/Descriptor.js
deleted file mode 100644
--- a/src/js/di/Descriptor.js
+++ /dev/null
@@ -1,4 +0,0 @@
-define([], function() {
- // abstract base type for descriptros
- return function() {};
-});
\ No newline at end of file
diff --git a/src/js/di/ReferenceDescriptor.js b/src/js/di/ReferenceDescriptor.js
deleted file mode 100644
--- a/src/js/di/ReferenceDescriptor.js
+++ /dev/null
@@ -1,90 +0,0 @@
-define([
- "../declare", "../safe", "./Descriptor", "./ActivationError", "./ValueDescriptor"
-],
-
-function(declare, safe, Descriptor, ActivationError, Value) {
- return declare(Descriptor, {
- _name : null,
- _lazy : false,
- _optional : false,
- _default : undefined,
-
- constructor : function(name, lazy, optional, def, services) {
- safe.argumentNotEmptyString(name, "name");
- this._name = name;
- this._lazy = Boolean(lazy);
- this._optional = Boolean(optional);
- this._default = def;
- this._services = services;
- },
-
- activate : function(context, name) {
- var me = this;
-
- context.enter(name, this, true);
-
- // добавляем сервисы
- if (me._services) {
- for ( var p in me._services) {
- var sv = me._services[p];
- context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false));
- }
- }
-
- if (me._lazy) {
- // сохраняем контекст активации
- context = context.clone();
- return function(cfg) {
- // защищаем контекст на случай исключения в процессе
- // активации
- var ct = context.clone();
- try {
- if (cfg)
- safe.each(cfg, function(v, k) {
- ct.register(k, v instanceof Descriptor ? v : new Value(v, false));
- });
- return me._optional ? ct.getService(me._name, me._default) : ct
- .getService(me._name);
- } catch (error) {
- throw new ActivationError(me._name, ct.getStack(), error);
- }
- };
- }
-
- var v = me._optional ? context.getService(me._name, me._default) : context
- .getService(me._name);
- context.leave(me);
- return v;
- },
-
- isInstanceCreated : function() {
- return false;
- },
-
- toString : function() {
- var opts = [];
- if (this._optional)
- opts.push("optional");
- if (this._lazy)
- opts.push("lazy");
-
- var parts = [
- "@ref "
- ];
- if (opts.length) {
- parts.push("{");
- parts.push(opts.join());
- parts.push("} ");
- }
-
- parts.push(this._name);
-
- if (!safe.isNull(this._default)) {
- parts.push(" = ");
- parts.push(this._default);
- }
-
- return parts.join("");
- }
- });
-});
\ No newline at end of file
diff --git a/src/js/di/ServiceDescriptor.js b/src/js/di/ServiceDescriptor.js
deleted file mode 100644
--- a/src/js/di/ServiceDescriptor.js
+++ /dev/null
@@ -1,289 +0,0 @@
-define(
- [
- "../declare",
- "../safe",
- "./Descriptor",
- "./ValueDescriptor"
- ],
-
- function (declare, safe, Descriptor, Value) {
- var SINGLETON_ACTIVATION = 1,
- CONTAINER_ACTIVATION = 2,
- CONTEXT_ACTIVATION = 3,
- CALL_ACTIVATION = 4,
- HIERARCHY_ACTIVATION = 5;
-
- var injectMethod = function (target, method, context, args) {
- var m = target[method];
- if (!m)
- throw new Error("Method '" + method + "' not found");
-
- if (args instanceof Array)
- m.apply(target, context.parse(args, "." + method));
- else
- m.call(target, context.parse(args, "." + method));
- };
-
- var makeClenupCallback = function (target, method) {
- if (typeof (method) === "string") {
- return function () {
- target[method]();
- };
- } else {
- return function () {
- method(target);
- };
- }
- };
-
- var cacheId = 0;
-
- var cls = declare(
- Descriptor, {
- _instance: null,
- _hasInstance: false,
- _activationType: CALL_ACTIVATION,
- _services: null,
- _type: null,
- _typeMap: null,
- _factory: null,
- _params: undefined,
- _inject: null,
- _cleanup: null,
- _cacheId: null,
- _owner: null,
-
- constructor: function (opts) {
- safe.argumentNotNull(opts, "opts");
- safe.argumentNotNull(opts.owner, "opts.owner");
-
- this._owner = opts.owner;
-
- if (!(opts.type || opts.factory))
- throw new Error(
- "Either a type or a factory must be specified");
-
- if (typeof (opts.type) === "string" && !opts.typeMap)
- throw new Error(
- "The typeMap is required when the type is specified by its name");
-
- if (opts.activation)
- this._activationType = opts.activation;
- if (opts.type)
- this._type = opts.type;
- if (opts.params)
- this._params = opts.params;
- if (opts.inject)
- this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
- if (opts.services)
- this._services = opts.services;
- if (opts.factory)
- this._factory = opts.factory;
- if (opts.typeMap)
- this._typeMap = opts.typeMap;
- if (opts.cleanup) {
- if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
- throw new Error(
- "The cleanup parameter must be either a function or a function name");
-
- this._cleanup = opts.cleanup;
- }
-
- this._cacheId = ++cacheId;
- },
-
- activate: function (context, name) {
-
- // if we have a local service records, register them first
-
- var instance;
-
- switch (this._activationType) {
- case 1: // SINGLETON
- // if the value is cached return it
- if (this._hasInstance)
- return this._instance;
-
- var tof = this._type || this._factory;
-
- // create the persistent cache identifier for the type
- if (safe.isPrimitive(tof))
- this._cacheId = this._type;
- else
- this._cacheId = safe.oid(tof);
-
- // singletons are bound to the root container
- var container = context.container.getRootContainer();
-
- if (container.has(this._cacheId)) {
- instance = container.get(this._cacheId);
- } else {
- instance = this._create(context, name);
- container.store(this._cacheId, instance);
- if (this._cleanup)
- container.onDispose(
- makeClenupCallback(instance, this._cleanup));
- }
-
- this._hasInstance = true;
- return (this._instance = instance);
-
- case 2: // CONTAINER
- //return a cached value
- if (this._hasInstance)
- return this._instance;
-
- // create an instance
- instance = this._create(context, name);
-
- // the instance is bound to the container
- if (this._cleanup)
- this._owner.onDispose(
- makeClenupCallback(instance, this._cleanup));
-
- // cache and return the instance
- this._hasInstance = true;
- return (this._instance = instance);
- case 3: // CONTEXT
- //return a cached value if one exists
- if (context.has(this._cacheId))
- return context.get(this._cacheId);
- // context context activated instances are controlled by callers
- return context.store(this._cacheId, this._create(
- context,
- name));
- case 4: // CALL
- // per-call created instances are controlled by callers
- return this._create(context, name);
- case 5: // HIERARCHY
- // hierarchy activated instances are behave much like container activated
- // except they are created and bound to the child container
-
- // return a cached value
- if (context.container.has(this._cacheId))
- return context.container.get(this._cacheId);
-
- instance = this._create(context, name);
-
- if (this._cleanup)
- context.container.onDispose(makeClenupCallback(
- instance,
- this._cleanup));
-
- return context.container.store(this._cacheId, instance);
- default:
- throw "Invalid activation type: " + this._activationType;
- }
- },
-
- isInstanceCreated: function () {
- return this._hasInstance;
- },
-
- getInstance: function () {
- return this._instance;
- },
-
- _create: function (context, name) {
- context.enter(name, this, Boolean(this._services));
-
- if (this._activationType != CALL_ACTIVATION &&
- context.visit(this._cacheId) > 0)
- throw new Error("Recursion detected");
-
- if (this._services) {
- for (var p in this._services) {
- var sv = this._services[p];
- context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false));
- }
- }
-
- var instance;
-
- if (!this._factory) {
- var ctor, type = this._type;
-
- if (typeof (type) === "string") {
- ctor = this._typeMap[type];
- if (!ctor)
- throw new Error("Failed to resolve the type '" +
- type + "'");
- } else {
- ctor = type;
- }
-
- if (this._params === undefined) {
- this._factory = function () {
- return new ctor();
- };
- } else if (this._params instanceof Array) {
- this._factory = function () {
- var inst = Object.create(ctor.prototype);
- var ret = ctor.apply(inst, arguments);
- return typeof (ret) === "object" ? ret : inst;
- };
- } else {
- this._factory = function (param) {
- return new ctor(param);
- };
- }
- }
-
- if (this._params === undefined) {
- instance = this._factory();
- } else if (this._params instanceof Array) {
- instance = this._factory.apply(this, context.parse(
- this._params,
- ".params"));
- } else {
- instance = this._factory(context.parse(
- this._params,
- ".params"));
- }
-
- if (this._inject) {
- this._inject.forEach(function (spec) {
- for (var m in spec)
- injectMethod(instance, m, context, spec[m]);
- });
- }
-
- context.leave();
-
- return instance;
- },
-
- // @constructor {singleton} foo/bar/Baz
- // @factory {singleton}
- toString: function () {
- var parts = [];
-
- parts.push(this._type ? "@constructor" : "@factory");
-
- parts.push(activationNames[this._activationType]);
-
- if (typeof (this._type) === "string")
- parts.push(this._type);
-
- return parts.join(" ");
- }
-
- });
-
- cls.SINGLETON = SINGLETON_ACTIVATION;
- cls.CONTAINER = CONTAINER_ACTIVATION;
- cls.CONTEXT = CONTEXT_ACTIVATION;
- cls.CALL = CALL_ACTIVATION;
- cls.HIERARCHY = HIERARCHY_ACTIVATION;
-
- var activationNames = [
- "",
- "{singleton}",
- "{container}",
- "{context}",
- "{call}",
- "{hierarchy}"
- ];
-
- return cls;
- });
\ No newline at end of file
diff --git a/src/js/di/ValueDescriptor.js b/src/js/di/ValueDescriptor.js
deleted file mode 100644
--- a/src/js/di/ValueDescriptor.js
+++ /dev/null
@@ -1,38 +0,0 @@
-define([ "../declare", "./Descriptor", "../safe" ],
-
-function(declare, Descriptor, safe) {
- return declare(Descriptor, {
- _value : undefined,
- _raw : false,
- constructor : function(value, raw) {
- this._value = value;
- this._raw = Boolean(raw);
- },
-
- activate : function(context, name) {
- context.enter(name, this);
- var v = this._raw ? this._value : context.parse(
- this._value,
- ".params");
- context.leave(this);
- return v;
- },
-
- isInstanceCreated : function() {
- return this._raw;
- },
-
- getInstance : function() {
- if (!this._raw)
- throw new Error("The instance isn't constructed");
- return this._value;
- },
-
- toString : function() {
- if (this._raw)
- return "@value {raw}";
- else
- return safe.isNull(this._value) ? "@value " : "@value";
- }
- });
-});
\ No newline at end of file
diff --git a/src/js/text/format-compile.js b/src/js/text/format-compile.js
deleted file mode 100644
--- a/src/js/text/format-compile.js
+++ /dev/null
@@ -1,101 +0,0 @@
-define(
- [],
- function() {
- var map = {
- "\\{" : "&curlopen;",
- "\\}" : "&curlclose;",
- "&" : "&",
- "\\:" : ":"
- };
-
- var rev = {
- curlopen : "{",
- curlclose : "}",
- amp : "&",
- colon : ":"
- };
-
- var espaceString = function(s) {
- if (!s)
- return s;
- return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n","\\n") + "'";
- };
-
- var encode = function(s) {
- if (!s)
- return s;
- return s.replace(/\\{|\\}|&|\\:|\n/g, function(m) {
- return map[m] || m;
- });
- };
-
- var decode = function(s) {
- if (!s)
- return s;
- return s.replace(/&(\w+);/g, function(m, $1) {
- return rev[$1] || m;
- });
- };
-
- var subst = function(s) {
- var i = s.indexOf(":"), name, pattern;
- if (i >= 0) {
- name = s.substr(0, i);
- pattern = s.substr(i + 1);
- } else {
- name = s;
- }
-
- if (pattern)
- return [
- espaceString(decode(name)),
- espaceString(decode(pattern)) ];
- else
- return [ espaceString(decode(name)) ];
- };
-
- var compile = function(str) {
- if (!str)
- return function() {};
-
- var chunks = encode(str).split("{"), chunk;
-
- var code = [ "var result=[];" ];
-
- for (var i = 0; i < chunks.length; i++) {
- chunk = chunks[i];
-
- if (i === 0) {
- if (chunk)
- code.push("result.push(" + espaceString(decode(chunk)) +
- ");");
- } else {
- var len = chunk.indexOf("}");
- if (len < 0)
- throw new Error("Unbalanced substitution #" + i);
-
- code.push("result.push(subst(" +
- subst(chunk.substr(0, len)).join(",") + "));");
- if (chunk.length > len + 1)
- code.push("result.push(" +
- espaceString(decode(chunk.substr(len + 1))) + ");");
- }
- }
-
- code.push("return result.join('');");
-
- /* jshint -W054 */
- return new Function("subst", code.join("\n"));
- };
-
- var cache = {};
-
- return function(template) {
- var compiled = cache[template];
- if (!compiled) {
- compiled = compile(template);
- cache[template] = compiled;
- }
- return compiled;
- };
- });
\ No newline at end of file
diff --git a/src/js/text/format.js b/src/js/text/format.js
deleted file mode 100644
--- a/src/js/text/format.js
+++ /dev/null
@@ -1,87 +0,0 @@
-define([
- "../safe",
- "./format-compile",
- "dojo/number",
- "dojo/date/locale",
- "dojo/_base/array" ], function(safe, compile, number, date, array) {
-
- // {short,medium,full,long}-{date,time}
- var convert = function(value, pattern) {
- if (!pattern)
- return value.toString();
-
- if (pattern.toLocaleLowerCase() == "json") {
- var cache = [];
- return JSON.stringify(value, function(k, v) {
- if (!safe.isPrimitive(v)) {
- var id = array.indexOf(cache, v);
- if (id >= 0)
- return "@ref-" + id;
- else
- return v;
- } else {
- return v;
- }
- },2);
- }
-
- if (safe.isNumber(value)) {
- var nopt = {};
- if (pattern.indexOf("!") === 0) {
- nopt.round = -1;
- pattern = pattern.substr(1);
- }
- nopt.pattern = pattern;
- return number.format(value, nopt);
- } else if (value instanceof Date) {
- var m = pattern.match(/^(\w+)-(\w+)$/);
- if (m)
- return date.format(value, {
- selector : m[2],
- formatLength : m[1]
- });
- else if (pattern == "iso")
- return value.toISOString();
- else
- return date.format(value, {
- selector : "date",
- datePattern : pattern
- });
- } else {
- return value.toString(pattern);
- }
- };
-
- function formatter(format) {
- var data;
-
- if (arguments.length <= 1)
- return format;
-
- data = Array.prototype.slice.call(arguments, 1);
-
- var template = compile(format);
-
- return template(function(name, pattern) {
- var value = data[name];
- return !safe.isNull(value) ? convert(value, pattern) : "";
- });
- }
-
- formatter.compile = function(format) {
- var template = compile(format);
-
- return function() {
- var data = arguments;
-
- return template(function(name, pattern) {
- var value = data[name];
- return !safe.isNull(value) ? convert(value, pattern) : "";
- });
- };
- };
-
- formatter.convert = convert;
-
- return formatter;
-});
\ No newline at end of file
diff --git a/src/js/text/template-compile.js b/src/js/text/template-compile.js
deleted file mode 100644
--- a/src/js/text/template-compile.js
+++ /dev/null
@@ -1,134 +0,0 @@
-define(
- ["dojo/request", "./format", "../log/trace!"],
- function (request, format, trace) {
-
- // разбивает строку шаблона на токены, возвращает контекст для
- // дальнейшей обработки в visitTemplate
- var parseTemplate = function (str) {
- var tokens = str.split(/(<%=|\[%=|<%|\[%|%\]|%>)/);
- var pos = -1;
- var data = [],
- code = [];
-
- return {
- next: function () {
- pos++;
- return pos < tokens.length;
- },
- token: function () {
- return tokens[pos];
- },
- pushData: function () {
- var i = data.length;
- data.push.apply(data, arguments);
- return i;
- },
- pushCode : function() {
- var i = code.length;
- code.push.apply(code, arguments);
- return i;
- },
- compile: function () {
- var text = "var $p = [];\n" +
- "var print = function(){\n" +
- " $p.push(format.apply(null,arguments));\n" +
- "};\n" +
- // Introduce the data as local variables using with(){}
- "with(obj){\n" +
- code.join("\n") +
- "}\n" +
- "return $p.join('');";
-
- try {
- var compiled = new Function("obj, format, $data", text);
- /**
- * Функция форматирования по шаблону
- *
- * @type{Function}
- * @param{Object} obj объект с параметрами для подстановки
- */
- return function (obj) {
- return compiled(obj || {}, format, data);
- };
- } catch (e) {
- trace.error([e]);
- trace.log([text, data]);
- throw e;
- }
- }
- }
- };
-
- function visitTemplate(context) {
- while (context.next()) {
- switch (context.token()) {
- case "<%":
- case "[%":
- visitCode(context);
- break;
- case "<%=":
- case "[%=":
- visitInline(context);
- break;
- default:
- visitTextFragment(context);
- break;
- }
- }
- }
-
- function visitInline(context) {
- var code = ["$p.push("];
- while (context.next()) {
- if (context.token() == "%>" || context.token() == "%]")
- break;
- code.push(context.token());
- }
- code.push(");");
- context.pushCode(code.join(''));
- }
-
- function visitCode(context) {
- var code = [];
- while (context.next()) {
- if (context.token() == "%>" || context.token() == "%]")
- break;
- code.push(context.token());
- }
- context.pushCode(code.join(''));
- }
-
- function visitTextFragment(context) {
- var i = context.pushData(context.token());
- context.pushCode("$p.push($data["+i+"]);");
- }
-
- var compile = function (str) {
- if (!str)
- return function() { return "";};
-
- var ctx = parseTemplate(str);
- visitTemplate(ctx);
- return ctx.compile();
- };
-
- var cache = {};
-
- compile.load = function (id, require, callback) {
- var url = require.toUrl(id);
- if (url in cache) {
- callback(cache[url]);
- } else {
- request(url).then(compile).then(function (tc) {
- callback(cache[url] = tc);
- }, function (err) {
- require.signal("error", [{
- error: err,
- src: 'implab/text/template-compile'
- }]);
- });
- }
- };
-
- return compile;
- });
\ No newline at end of file
diff --git a/src/ts/Cancellation.ts b/src/main/ts/Cancellation.ts
rename from src/ts/Cancellation.ts
rename to src/main/ts/Cancellation.ts
--- a/src/ts/Cancellation.ts
+++ b/src/main/ts/Cancellation.ts
@@ -40,21 +40,21 @@ export class Cancellation implements ICa
else
this._cbs.push(cb);
- let me = this;
+ const me = this;
return {
- destroy() {
- me._unregister(cb);
- }
+ destroy() {
+ me._unregister(cb);
+ }
};
}
}
-
+
private _unregister(cb) {
- if(this._cbs) {
- let i = this._cbs.indexOf(cb);
- if ( i>=0 )
- this._cbs.splice(i,1);
- }
+ if (this._cbs) {
+ const i = this._cbs.indexOf(cb);
+ if (i >= 0)
+ this._cbs.splice(i, 1);
+ }
}
private _cancel(reason) {
@@ -63,7 +63,6 @@ export class Cancellation implements ICa
this._reason = (reason = reason || new Error("Operation cancelled"));
-
if (this._cbs) {
this._cbs.forEach(cb => cb(reason));
this._cbs = null;
@@ -83,7 +82,7 @@ export class Cancellation implements ICa
},
register(_cb: (e: any) => void): IDestroyable {
- return destroyed;
+ return destroyed;
}
};
-}
\ No newline at end of file
+}
diff --git a/src/ts/Observable.ts b/src/main/ts/Observable.ts
rename from src/ts/Observable.ts
rename to src/main/ts/Observable.ts
--- a/src/ts/Observable.ts
+++ b/src/main/ts/Observable.ts
@@ -1,36 +1,30 @@
-import { IObservable, IDestroyable, ICancellation } from './interfaces';
-import { Cancellation } from './Cancellation'
-import { argumentNotNull } from './safe';
-
+import { IObservable, IDestroyable, ICancellation } from "./interfaces";
+import { Cancellation } from "./Cancellation";
+import { argumentNotNull } from "./safe";
-interface Handler {
- (x: T): void
-}
+type Handler = (x: T) => void;
-interface Initializer {
- (notify: Handler, error?: (e: any) => void, complete?: () => void): void;
-}
+type Initializer = (notify: Handler, error?: (e: any) => void, complete?: () => void) => void;
// TODO: think about to move this interfaces.ts and make it public
interface IObserver {
- next(event: T): void
+ next(event: T): void;
- error(e: any): void
+ error(e: any): void;
- complete(): void
+ complete(): void;
}
-const noop = () => {};
+const noop = () => { };
export class Observable implements IObservable {
private _once = new Array>();
private _observers = new Array>();
+ private _complete: boolean;
- private _complete: boolean
-
- private _error: any
+ private _error: any;
constructor(func?: Initializer) {
if (func)
@@ -43,21 +37,21 @@ export class Observable implements IO
/**
* Registers handlers for the current observable object.
- *
+ *
* @param next the handler for events
* @param error the handler for a error
* @param complete the handler for a completion
* @returns {IDestroyable} the handler for the current subscription, this
* handler can be used to unsubscribe from events.
- *
+ *
*/
on(next: Handler, error?: Handler, complete?: () => void): IDestroyable {
argumentNotNull(next, "next");
- let me = this;
+ const me = this;
- let observer: IObserver & IDestroyable = {
- next: next,
+ const observer: IObserver & IDestroyable = {
+ next,
error: error ? error.bind(null) : noop,
complete: complete ? complete.bind(null) : noop,
@@ -68,7 +62,6 @@ export class Observable implements IO
this._addObserver(observer);
-
return observer;
}
@@ -90,19 +83,19 @@ export class Observable implements IO
/**
* Waits for the next event. This method can't be used to read messages
* as a sequence since it can skip some messages between calls.
- *
+ *
* @param ct a cancellation token
*/
next(ct: ICancellation = Cancellation.none): Promise {
return new Promise((resolve, reject) => {
- let observer: IObserver = {
+ const observer: IObserver = {
next: resolve,
error: reject,
complete: () => reject("No more events are available")
};
if (this._addOnce(observer) && ct.isSupported()) {
- ct.register((e) => {
+ ct.register(e => {
this._removeOnce(observer);
reject(e);
});
@@ -131,48 +124,44 @@ export class Observable implements IO
}
private _removeOnce(d: IObserver) {
- let i = this._once.indexOf(d);
+ const i = this._once.indexOf(d);
if (i >= 0)
this._once.splice(i, 1);
}
private _removeObserver(d: IObserver) {
- let i = this._observers.indexOf(d);
+ const i = this._observers.indexOf(d);
if (i >= 0)
this._observers.splice(i, 1);
}
private _notify(guard: (observer: IObserver) => void) {
- if (this._once.length) {
- for (let i = 0; i < this._once.length; i++)
- guard(this._once[i]);
- this._once = [];
- }
+ this._once.forEach(guard);
+ this._once = [];
- for (let i = 0; i < this._observers.length; i++)
- guard(this._observers[i]);
+ this._observers.forEach(guard);
}
protected _notifyNext(evt: T) {
- let guard = (observer: IObserver) => {
+ const guard = (observer: IObserver) => {
try {
observer.next(evt);
} catch (e) {
this.onObserverException(e);
}
- }
+ };
this._notify(guard);
}
protected _notifyError(e: any) {
- let guard = (observer: IObserver) => {
+ const guard = (observer: IObserver) => {
try {
observer.error(e);
} catch (e) {
this.onObserverException(e);
}
- }
+ };
this._notify(guard);
this._observers = [];
@@ -180,16 +169,16 @@ export class Observable implements IO
}
protected _notifyCompleted() {
- let guard = (observer: IObserver) => {
+ const guard = (observer: IObserver) => {
try {
observer.complete();
} catch (e) {
this.onObserverException(e);
}
- }
+ };
this._notify(guard);
this._observers = [];
this._complete = true;
}
-}
\ No newline at end of file
+}
diff --git a/src/ts/Uuid.ts b/src/main/ts/Uuid.ts
rename from src/ts/Uuid.ts
rename to src/main/ts/Uuid.ts
--- a/src/ts/Uuid.ts
+++ b/src/main/ts/Uuid.ts
@@ -6,9 +6,11 @@
// Copyright (c) 2010-2012 Robert Kieffer
// MIT License - http://opensource.org/licenses/mit-license.php
-declare var window: any;
+declare const window: any;
+declare const require;
+declare const Buffer;
-let _window : any = 'undefined' !== typeof window ? window : null;
+const _window: any = "undefined" !== typeof window ? window : null;
// Unique ID creation requires a high quality random # generator. We
// feature
@@ -19,14 +21,14 @@ let _rng;
function setupBrowser() {
// Allow for MSIE11 msCrypto
- let _crypto = _window.crypto || _window.msCrypto;
+ const _crypto = _window.crypto || _window.msCrypto;
if (!_rng && _crypto && _crypto.getRandomValues) {
// WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
//
// Moderately fast, high quality
try {
- let _rnds8 = new Uint8Array(16);
+ const _rnds8 = new Uint8Array(16);
_rng = function whatwgRNG() {
_crypto.getRandomValues(_rnds8);
return _rnds8;
@@ -41,9 +43,9 @@ function setupBrowser() {
// If all else fails, use Math.random(). It's fast, but is of
// unspecified
// quality.
- let _rnds = new Array(16);
- _rng = function () {
- for (var i = 0, r; i < 16; i++) {
+ const _rnds = new Array(16);
+ _rng = () => {
+ for (let i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) {
r = Math.random() * 0x100000000;
}
@@ -52,7 +54,7 @@ function setupBrowser() {
return _rnds;
};
- if ('undefined' !== typeof console && console.warn) {
+ if ("undefined" !== typeof console && console.warn) {
console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
}
}
@@ -63,12 +65,10 @@ function setupNode() {
// http://nodejs.org/docs/v0.6.2/api/crypto.html
//
// Moderately fast, high quality
- if ('function' === typeof require) {
+ if ("function" === typeof require) {
try {
- let _rb = require('crypto').randomBytes;
- _rng = _rb && function () {
- return _rb(16);
- };
+ const _rb = require("crypto").randomBytes;
+ _rng = _rb && (() => _rb(16));
_rng();
} catch (e) { /**/ }
}
@@ -81,22 +81,22 @@ if (_window) {
}
// Buffer class to use
-let BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
+const BufferClass = ("function" === typeof Buffer) ? Buffer : Array;
// Maps for number <-> hex string conversion
-let _byteToHex = [];
-let _hexToByte = {};
+const _byteToHex = [];
+const _hexToByte = {};
for (let i = 0; i < 256; i++) {
_byteToHex[i] = (i + 0x100).toString(16).substr(1);
_hexToByte[_byteToHex[i]] = i;
}
// **`parse()` - Parse a UUID into it's component bytes**
-function parse(s, buf?, offset?) : Array {
- let i = (buf && offset) || 0, ii = 0;
+export function _parse(s, buf?, offset?): Array {
+ const i = (buf && offset) || 0; let ii = 0;
buf = buf || [];
- s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
+ s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {
if (ii < 16) { // Don't overflow!
buf[i + ii++] = _hexToByte[oct];
}
@@ -111,12 +111,12 @@ function parse(s, buf?, offset?) : Array
}
// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
-function unparse(buf, offset?) : string {
- let i = offset || 0, bth = _byteToHex;
+function _unparse(buf, offset?): string {
+ let i = offset || 0; const bth = _byteToHex;
return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
- bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' +
- bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] +
- bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] +
+ bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" +
+ bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] +
+ bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
}
@@ -126,11 +126,11 @@ function unparse(buf, offset?) : string
// and http://docs.python.org/library/uuid.html
// random #'s we need to init node and clockseq
-let _seedBytes = _rng();
+const _seedBytes = _rng();
// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
// 1)
-let _nodeId = [
+const _nodeId = [
_seedBytes[0] | 0x01,
_seedBytes[1],
_seedBytes[2],
@@ -143,12 +143,12 @@ let _nodeId = [
let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
// Previous uuid creation time
-let _lastMSecs = 0, _lastNSecs = 0;
+let _lastMSecs = 0; let _lastNSecs = 0;
// See https://github.com/broofa/node-uuid for API details
-function v1(options?, buf?, offset?) : string {
+export function _v1(options?, buf?, offset?): string {
let i = buf && offset || 0;
- let b = buf || [];
+ const b = buf || [];
options = options || {};
@@ -170,7 +170,7 @@ function v1(options?, buf?, offset?) : s
let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
// Time since last uuid creation (in msecs)
- let dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
+ const dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
// Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options.clockseq == null) {
@@ -187,7 +187,7 @@ function v1(options?, buf?, offset?) : s
// Per 4.2.1.2 Throw error if too many uuids are requested
if (nsecs >= 10000) {
throw new Error(
- 'uuid.v1(): Can\'t create more than 10M uuids/sec');
+ "uuid.v1(): Can't create more than 10M uuids/sec");
}
_lastMSecs = msecs;
@@ -198,14 +198,14 @@ function v1(options?, buf?, offset?) : s
msecs += 12219292800000;
// `time_low`
- let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
+ const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
b[i++] = tl >>> 24 & 0xff;
b[i++] = tl >>> 16 & 0xff;
b[i++] = tl >>> 8 & 0xff;
b[i++] = tl & 0xff;
// `time_mid`
- let tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
+ const tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
b[i++] = tmh >>> 8 & 0xff;
b[i++] = tmh & 0xff;
@@ -220,28 +220,28 @@ function v1(options?, buf?, offset?) : s
b[i++] = clockseq & 0xff;
// `node`
- let node = options.node || _nodeId;
+ const node = options.node || _nodeId;
for (let n = 0; n < 6; n++) {
b[i + n] = node[n];
}
- return buf ? buf : unparse(b);
+ return buf ? buf : _unparse(b);
}
// **`v4()` - Generate random UUID**
// See https://github.com/broofa/node-uuid for API details
-function v4(options?, buf?, offset?) : string {
+export function _v4(options?, buf?, offset?): string {
// Deprecated - 'format' argument, as supported in v1.2
- let i = buf && offset || 0;
+ const i = buf && offset || 0;
- if (typeof (options) === 'string') {
- buf = (options === 'binary') ? new BufferClass(16) : null;
+ if (typeof (options) === "string") {
+ buf = (options === "binary") ? new BufferClass(16) : null;
options = null;
}
options = options || {};
- let rnds = options.random || (options.rng || _rng)();
+ const rnds = options.random || (options.rng || _rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
@@ -254,29 +254,16 @@ function v4(options?, buf?, offset?) : s
}
}
- return buf || unparse(rnds);
+ return buf || _unparse(rnds);
+}
+
+export function Uuid() {
+ return _v4();
}
-// Export public API
-const empty = "00000000-0000-0000-0000-000000000000";
-
-interface uuid {
- (options?, buf?, offset?) : string;
- v1(options?, buf?, offset?) : string;
- v4(options?, buf?, offset?) : string;
- readonly empty: string;
- parse(s, buf?, offset?) : Array;
- unparse(buf, offset?) : string;
+export namespace Uuid {
+ export const v4 = _v4;
+ export const v1 = _v1;
+ export const empty = "00000000-0000-0000-0000-000000000000";
+ export const parse = _parse;
}
-
-export = (() =>{
- var f : any = function(options?, buf?, offset?) : string {
- return v4(options, buf, offset);
- };
- f.v1 = v1;
- f.v4 = v4;
- f.empty = empty;
- f.parse = parse;
- f.unparse = unparse;
- return f;
-})();
\ No newline at end of file
diff --git a/src/ts/components/ActivatableMixin.ts b/src/main/ts/components/ActivatableMixin.ts
rename from src/ts/components/ActivatableMixin.ts
rename to src/main/ts/components/ActivatableMixin.ts
--- a/src/ts/components/ActivatableMixin.ts
+++ b/src/main/ts/components/ActivatableMixin.ts
@@ -1,11 +1,11 @@
-import { IActivationController, IActivatable, ICancellation } from '../interfaces';
-import { AsyncComponent } from './AsyncComponent';
-import { Cancellation } from '../Cancellation';
-import { TraceSource } from '../log/TraceSource';
+import { IActivationController, IActivatable, ICancellation } from "../interfaces";
+import { AsyncComponent } from "./AsyncComponent";
+import { Cancellation } from "../Cancellation";
+import { TraceSource } from "../log/TraceSource";
type Constructor = new (...args: any[]) => T;
-const log = TraceSource.get('@implab/core/components/ActivatableMixin');
+const log = TraceSource.get("@implab/core/components/ActivatableMixin");
export function ActivatableMixin>(Base: TBase) {
return class extends Base implements IActivatable {
@@ -77,7 +77,7 @@ export function ActivatableMixin = Promise.resolve();
- getCompletion() { return this._completion };
+ getCompletion() { return this._completion; }
runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
// create inner cancellation bound to the passed cancellation token
let h: IDestroyable;
- let inner = new Cancellation(cancel => {
+ const inner = new Cancellation(cancel => {
this._cancel = cancel;
h = ct.register(cancel);
});
// TODO create cancellation source here
- let guard = async () => {
+ const guard = async () => {
try {
await op(inner);
} finally {
@@ -28,7 +28,7 @@ export class AsyncComponent implements I
destroy(h);
this._cancel = null;
}
- }
+ };
return this._completion = guard();
}
@@ -37,4 +37,4 @@ export class AsyncComponent implements I
if (this._cancel)
this._cancel(reason);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/ts/di.ts b/src/main/ts/di.ts
new file mode 100644
diff --git a/src/main/ts/di/ActivationContext.ts b/src/main/ts/di/ActivationContext.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ActivationContext.ts
@@ -0,0 +1,132 @@
+import { TraceSource } from "../log/TraceSource";
+import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
+import { Descriptor, ServiceMap } from "./interfaces";
+import { Container } from "./Container";
+
+const trace = TraceSource.get("@implab/core/di/ActivationContext");
+
+export interface ActivationContextInfo {
+ name: string;
+
+ service: string;
+
+ scope: ServiceMap;
+}
+
+export class ActivationContext {
+ _cache: object;
+
+ _services: ServiceMap;
+
+ _stack: ActivationContextInfo[];
+
+ _visited: object;
+
+ _name: string;
+
+ _localized: boolean;
+
+ container: Container;
+
+ constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
+ argumentNotNull(container, "container");
+ argumentNotNull(services, "services");
+
+ this._name = name;
+ this._visited = visited || {};
+ this._stack = [];
+ this._cache = cache || {};
+ this._services = services;
+ this.container = container;
+ }
+
+ getName() {
+ return this._name;
+ }
+
+ resolve(name, def?): any {
+ const d = this._services[name];
+
+ if (!d)
+ if (arguments.length > 1)
+ return def;
+ else
+ throw new Error(`Service ${name} not found`);
+
+ return this.activate(d, name);
+ }
+
+ /**
+ * registers services local to the the activation context
+ *
+ * @name{string} the name of the service
+ * @service{string} the service descriptor to register
+ */
+ register(name: string, service: Descriptor) {
+ argumentNotEmptyString(name, "name");
+
+ this._services[name] = service;
+ }
+
+ clone() {
+ return new ActivationContext(
+ this.container,
+ this._services,
+ this._name,
+ this._cache,
+ this._visited
+ );
+ }
+
+ has(id: string) {
+ return id in this._cache;
+ }
+
+ get(id: string) {
+ return this._cache[id];
+ }
+
+ store(id: string, value) {
+ return (this._cache[id] = value);
+ }
+
+ activate(d: Descriptor, name: string) {
+ if (trace.isLogEnabled())
+ trace.log(`enter ${name} ${d}`);
+
+ this.enter(name, d.toString());
+ const v = d.activate(this);
+ this.leave();
+
+ if (trace.isLogEnabled())
+ trace.log(`leave ${name}`);
+
+ return v;
+ }
+
+ visit(id: string) {
+ const count = this._visited[id] || 0;
+ this._visited[id] = count + 1;
+ return count;
+ }
+
+ getStack() {
+ return this._stack.slice().reverse();
+ }
+
+ private enter(name: string, service: string) {
+ this._stack.push({
+ name,
+ service,
+ scope: this._services
+ });
+ this._name = name;
+ this._services = Object.create(this._services);
+ }
+
+ private leave() {
+ const ctx = this._stack.pop();
+ this._services = ctx.scope;
+ this._name = ctx.name;
+ }
+}
diff --git a/src/main/ts/di/ActivationError.ts b/src/main/ts/di/ActivationError.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ActivationError.ts
@@ -0,0 +1,36 @@
+import { ActivationContextInfo } from "./ActivationContext";
+
+export class ActivationError {
+ activationStack: ActivationContextInfo[];
+
+ service: string;
+
+ innerException: any;
+
+ message: string;
+
+ constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
+ this.message = "Failed to activate the service";
+ this.activationStack = activationStack;
+ this.service = service;
+ this.innerException = innerException;
+ }
+
+ toString() {
+ const parts = [this.message];
+ if (this.service)
+ parts.push("when activating: " + this.service.toString());
+
+ if (this.innerException)
+ parts.push("caused by: " + this.innerException.toString());
+
+ if (this.activationStack) {
+ parts.push("at");
+ this.activationStack
+ .forEach(x => parts.push(` ${x.name} ${x.service}`));
+
+ }
+
+ return parts.join("\n");
+ }
+}
diff --git a/src/main/ts/di/AggregateDescriptor.ts b/src/main/ts/di/AggregateDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/AggregateDescriptor.ts
@@ -0,0 +1,37 @@
+import { Descriptor, isDescriptor } from "./interfaces";
+import { ActivationContext } from "./ActivationContext";
+import { isPrimitive } from "../safe";
+
+export class AggregateDescriptor implements Descriptor {
+ _value: object;
+
+ constructor(value: object) {
+ this._value = value;
+ }
+
+ activate(context: ActivationContext) {
+ return this._parse(this._value, context, "$value");
+ }
+
+ // TODO: make async
+ _parse(value, context: ActivationContext, path: string) {
+ if (isPrimitive(value))
+ return value;
+
+ if (isDescriptor(value))
+ return context.activate(value, path);
+
+ if (value instanceof Array)
+ return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
+
+ const t = {};
+ for (const p of Object.keys(value))
+ t[p] = this._parse(value[p], context, `${path}.${p}`);
+ return t;
+
+ }
+
+ toString() {
+ return "@walk";
+ }
+}
diff --git a/src/main/ts/di/ConfigError.ts b/src/main/ts/di/ConfigError.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ConfigError.ts
@@ -0,0 +1,12 @@
+export class ConfigError extends Error {
+ inner;
+
+ path: string;
+
+ configName: string;
+
+ constructor(message: string, inner?) {
+ super(message);
+ this.inner = inner;
+ }
+}
diff --git a/src/main/ts/di/Configuration.ts b/src/main/ts/di/Configuration.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/Configuration.ts
@@ -0,0 +1,348 @@
+import {
+ ServiceRegistration,
+ TypeRegistration,
+ FactoryRegistration,
+ ServiceMap,
+ isDescriptor,
+ isDependencyRegistration,
+ DependencyRegistration,
+ ValueRegistration,
+ ActivationType,
+ isValueRegistration,
+ isTypeRegistration,
+ isFactoryRegistration
+} from "./interfaces";
+
+import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
+import { AggregateDescriptor } from "./AggregateDescriptor";
+import { ValueDescriptor } from "./ValueDescriptor";
+import { Container } from "./Container";
+import { ReferenceDescriptor } from "./ReferenceDescriptor";
+import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
+import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
+import { TraceSource } from "../log/TraceSource";
+import { ConfigError } from "./ConfigError";
+import { Cancellation } from "../Cancellation";
+
+const trace = TraceSource.get("@implab/core/di/Configuration");
+
+declare const define;
+declare const require;
+
+function hasAmdLoader() {
+ return (typeof define === "function" && define.amd);
+}
+
+async function mapAll(data: object | any[], map?: (v, k) => any): Promise {
+ if (data instanceof Array) {
+ return Promise.all(map ? data.map(map) : data);
+ } else {
+ const keys = Object.keys(data);
+
+ const o: any = {};
+
+ await Promise.all(keys.map(async k => {
+ const v = map ? map(data[k], k) : data[k];
+ o[k] = isPromise(v) ? await v : v;
+ }));
+
+ return o;
+ }
+}
+
+type Resolver = (qname: string) => any;
+
+type _key = string | number;
+
+export class Configuration {
+
+ _hasInnerDescriptors = false;
+
+ _container: Container;
+
+ _path: Array<_key>;
+
+ _configName: string;
+
+ _require: Resolver;
+
+ constructor(container: Container) {
+ argumentNotNull(container, container);
+ this._container = container;
+ this._path = [];
+ }
+
+ async loadConfiguration(moduleName: string, ct = Cancellation.none) {
+ argumentNotEmptyString(moduleName, "moduleName");
+ // TODO remove the code below somewehere else
+ if (hasAmdLoader()) {
+ // if we have a requirejs loader, use it directly
+ // don't rely on typescript 'import' function
+ const m = await new Promise(cb => require(["./RequireJsHelper"], cb));
+ const r = m.makeResolver(require);
+ const config = await r(moduleName);
+
+ return this.applyConfiguration(
+ config,
+ m.makeResolver(await m.createContextRequire(moduleName))
+ );
+ } else {
+ throw new Error("This feature is supported only with the amd loader");
+ }
+ }
+
+ async applyConfiguration(data: object, resolver?: Resolver, ct = Cancellation.none) {
+ argumentNotNull(data, "data");
+
+ trace.log("applyConfiguration");
+
+ this._configName = "$";
+
+ if (resolver)
+ this._require = resolver;
+
+ let services: ServiceMap;
+
+ try {
+ services = await this._visitRegistrations(data, "$");
+ } catch (e) {
+ throw this._makeError(e);
+ }
+
+ this._container.register(services);
+ }
+
+ _makeError(inner) {
+ const e = new ConfigError("Failed to load configuration", inner);
+ e.configName = this._configName;
+ e.path = this._makePath();
+ return e;
+ }
+
+ _makePath() {
+ return this._path
+ .reduce(
+ (prev, cur) => typeof cur === "number" ?
+ `${prev}[${cur}]` :
+ `${prev}.${cur}`
+ )
+ .toString();
+ }
+
+ async _resolveType(moduleName: string, localName: string) {
+ trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
+ try {
+ const m = await this._loadModule(moduleName);
+ return localName ? get(localName, m) : m;
+ } catch (e) {
+ trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
+ throw e;
+ }
+ }
+
+ async _loadModule(moduleName: string) {
+ trace.debug("loadModule {0}", moduleName);
+
+ const m = await this._require(moduleName);
+
+ return m;
+ }
+
+ async _visitRegistrations(data, name: _key) {
+ this._enter(name);
+
+ if (data.constructor &&
+ data.constructor.prototype !== Object.prototype)
+ throw new Error("Configuration must be a simple object");
+
+ const o: ServiceMap = {};
+ const keys = Object.keys(data);
+
+ const services = await mapAll(data, async (v, k) => {
+ const d = await this._visit(v, k);
+ return isDescriptor(d) ? d : new AggregateDescriptor(d);
+ }) as ServiceMap;
+
+ this._leave();
+
+ return services;
+ }
+
+ _enter(name: _key) {
+ this._path.push(name);
+ trace.debug(">{0}", name);
+ }
+
+ _leave() {
+ const name = this._path.pop();
+ trace.debug("<{0}", name);
+ }
+
+ async _visit(data, name: string): Promise {
+ if (isPrimitive(data) || isDescriptor(data))
+ return data;
+
+ if (isDependencyRegistration(data)) {
+ return this._visitDependencyRegistration(data, name);
+ } else if (isValueRegistration(data)) {
+ return this._visitValueRegistration(data, name);
+ } else if (isTypeRegistration(data)) {
+ return this._visitTypeRegistration(data, name);
+ } else if (isFactoryRegistration(data)) {
+ return this._visitFactoryRegistration(data, name);
+ } else if (data instanceof Array) {
+ return this._visitArray(data, name);
+ }
+
+ return this._visitObject(data, name);
+ }
+
+ async _visitObject(data: object, name: _key) {
+ if (data.constructor &&
+ data.constructor.prototype !== Object.prototype)
+ return new ValueDescriptor(data);
+
+ this._enter(name);
+
+ const v = await mapAll(data, delegate(this, "_visit"));
+
+ // TODO: handle inline descriptors properly
+ // const ex = {
+ // activate(ctx) {
+ // const value = ctx.activate(this.prop, "prop");
+ // // some code
+ // },
+ // // will be turned to ReferenceDescriptor
+ // prop: { $dependency: "depName" }
+ // };
+
+ this._leave();
+ return v;
+ }
+
+ async _visitArray(data: any[], name: _key) {
+ if (data.constructor &&
+ data.constructor.prototype !== Array.prototype)
+ return new ValueDescriptor(data);
+
+ this._enter(name);
+
+ const v = await mapAll(data, delegate(this, "_visit"));
+ this._leave();
+
+ return v;
+ }
+
+ _makeServiceParams(data: ServiceRegistration) {
+ const opts: any = {
+ owner: this._container
+ };
+ if (data.services)
+ opts.services = this._visitRegistrations(data.services, "services");
+
+ if (data.inject) {
+ this._path.push("inject");
+ opts.inject = mapAll(
+ data.inject instanceof Array ?
+ data.inject :
+ [data.inject],
+ delegate(this, "_visitObject")
+ );
+ this._leave();
+ }
+
+ if ("params" in data)
+ opts.params = data.params instanceof Array ?
+ this._visitArray(data.params, "params") :
+ this._visit(data.params, "params");
+
+ if (data.activation) {
+ if (typeof (data.activation) === "string") {
+ switch (data.activation.toLowerCase()) {
+ case "singleton":
+ opts.activation = ActivationType.Singleton;
+ break;
+ case "container":
+ opts.activation = ActivationType.Container;
+ break;
+ case "hierarchy":
+ opts.activation = ActivationType.Hierarchy;
+ break;
+ case "context":
+ opts.activation = ActivationType.Context;
+ break;
+ case "call":
+ opts.activation = ActivationType.Call;
+ break;
+ default:
+ throw new Error("Unknown activation type: " +
+ data.activation);
+ }
+ } else {
+ opts.activation = Number(data.activation);
+ }
+ }
+
+ if (data.cleanup)
+ opts.cleanup = data.cleanup;
+
+ return opts;
+ }
+
+ async _visitValueRegistration(data: ValueRegistration, name: _key) {
+ this._enter(name);
+ const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
+ this._leave();
+ return d;
+ }
+
+ async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
+ argumentNotEmptyString(data && data.$dependency, "data.$dependency");
+ this._enter(name);
+ const d = new ReferenceDescriptor({
+ name: data.$dependency,
+ lazy: data.lazy,
+ optional: data.optional,
+ default: data.default,
+ services: data.services && await this._visitRegistrations(data.services, "services")
+ });
+ this._leave();
+ return d;
+ }
+
+ async _visitTypeRegistration(data: TypeRegistration, name: _key) {
+ argumentNotNull(data.$type, "data.$type");
+ this._enter(name);
+
+ const opts = this._makeServiceParams(data);
+ if (data.$type instanceof Function) {
+ opts.type = data.$type;
+ } else {
+ const [moduleName, typeName] = data.$type.split(":", 2);
+ opts.type = this._resolveType(moduleName, typeName);
+ }
+
+ const d = new TypeServiceDescriptor(
+ await mapAll(opts)
+ );
+
+ this._leave();
+
+ return d;
+ }
+
+ async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
+ argumentOfType(data.$factory, Function, "data.$type");
+ this._enter(name);
+
+ const opts = this._makeServiceParams(data);
+ opts.factory = opts.$factory;
+
+ const d = new FactoryServiceDescriptor(
+ await mapAll(opts)
+ );
+
+ this._leave();
+ return d;
+ }
+}
diff --git a/src/main/ts/di/Container.ts b/src/main/ts/di/Container.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/Container.ts
@@ -0,0 +1,128 @@
+import { ActivationContext } from "./ActivationContext";
+import { ValueDescriptor } from "./ValueDescriptor";
+import { ActivationError } from "./ActivationError";
+import { isDescriptor, ServiceMap } from "./interfaces";
+import { TraceSource } from "../log/TraceSource";
+import { Configuration } from "./Configuration";
+import { Cancellation } from "../Cancellation";
+
+const trace = TraceSource.get("@implab/core/di/ActivationContext");
+
+export class Container {
+ _services: ServiceMap;
+
+ _cache: object;
+
+ _cleanup: (() => void)[];
+
+ _root: Container;
+
+ _parent: Container;
+
+ constructor(parent?: Container) {
+ this._parent = parent;
+ this._services = parent ? Object.create(parent._services) : {};
+ this._cache = {};
+ this._cleanup = [];
+ this._root = parent ? parent.getRootContainer() : this;
+ this._services.container = new ValueDescriptor(this);
+ }
+
+ getRootContainer() {
+ return this._root;
+ }
+
+ getParent() {
+ return this._parent;
+ }
+
+ resolve(name: string, def?) {
+ trace.debug("resolve {0}", name);
+ const d = this._services[name];
+ if (d === undefined) {
+ if (arguments.length > 1)
+ return def;
+ else
+ throw new Error("Service '" + name + "' isn't found");
+ }
+
+ const context = new ActivationContext(this, this._services);
+ try {
+ return context.activate(d, name);
+ } catch (error) {
+ throw new ActivationError(name, context.getStack(), error);
+ }
+ }
+
+ /**
+ * @deprecated use resolve() method
+ */
+ getService() {
+ return this.resolve.apply(this, arguments);
+ }
+
+ register(nameOrCollection, service?) {
+ if (arguments.length === 1) {
+ const data = nameOrCollection;
+ for (const name in data)
+ this.register(name, data[name]);
+ } else {
+ if (!isDescriptor(service))
+ throw new Error("The service parameter must be a descriptor");
+
+ this._services[nameOrCollection] = service;
+ }
+ return this;
+ }
+
+ onDispose(callback) {
+ if (!(callback instanceof Function))
+ throw new Error("The callback must be a function");
+ this._cleanup.push(callback);
+ }
+
+ dispose() {
+ if (this._cleanup) {
+ for (const f of this._cleanup)
+ f();
+ this._cleanup = null;
+ }
+ }
+
+ /**
+ * @param{String|Object} config
+ * The configuration of the contaier. Can be either a string or an object,
+ * if the configuration is an object it's treated as a collection of
+ * services which will be registed in the contaier.
+ *
+ * @param{Function} opts.contextRequire
+ * The function which will be used to load a configuration or types for services.
+ *
+ */
+ async configure(config: string | object, opts?, ct = Cancellation.none) {
+ const c = new Configuration(this);
+
+ if (typeof (config) === "string") {
+ return c.loadConfiguration(config, ct);
+ } else {
+ return c.applyConfiguration(config, opts && opts.contextRequire, ct);
+ }
+ }
+
+ createChildContainer() {
+ return new Container(this);
+ }
+
+ has(id) {
+ return id in this._cache;
+ }
+
+ get(id) {
+ return this._cache[id];
+ }
+
+ store(id, value) {
+ return (this._cache[id] = value);
+ }
+
+}
diff --git a/src/main/ts/di/FactoryServiceDescriptor.ts b/src/main/ts/di/FactoryServiceDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/FactoryServiceDescriptor.ts
@@ -0,0 +1,23 @@
+import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
+import { Factory } from "../interfaces";
+import { argumentNotNull, oid } from "../safe";
+import { ActivationType } from "./interfaces";
+
+export interface FactoryServiceDescriptorParams extends ServiceDescriptorParams {
+ factory: Factory;
+}
+
+export class FactoryServiceDescriptor extends ServiceDescriptor {
+ constructor(opts: FactoryServiceDescriptorParams) {
+ super(opts);
+
+ argumentNotNull(opts && opts.factory, "opts.factory");
+
+ // bind to null
+ this._factory = () => opts.factory();
+
+ if (opts.activation === ActivationType.Singleton) {
+ this._cacheId = oid(opts.factory);
+ }
+ }
+}
diff --git a/src/main/ts/di/ReferenceDescriptor.ts b/src/main/ts/di/ReferenceDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ReferenceDescriptor.ts
@@ -0,0 +1,100 @@
+import { isNull, argumentNotEmptyString, each } from "../safe";
+import { ActivationContext } from "./ActivationContext";
+import { ServiceMap, Descriptor } from "./interfaces";
+import { ActivationError } from "./ActivationError";
+
+export interface ReferenceDescriptorParams {
+ name: string;
+ lazy?: boolean;
+ optional?: boolean;
+ default?;
+ services?: ServiceMap;
+}
+
+export class ReferenceDescriptor implements Descriptor {
+ _name: string;
+
+ _lazy = false;
+
+ _optional = false;
+
+ _default: any;
+
+ _services: ServiceMap;
+
+ constructor(opts: ReferenceDescriptorParams) {
+ argumentNotEmptyString(opts && opts.name, "opts.name");
+ this._name = opts.name;
+ this._lazy = !!opts.lazy;
+ this._optional = !!opts.optional;
+ this._default = opts.default;
+ this._services = opts.services;
+ }
+
+ activate(context: ActivationContext, name: string) {
+ // добавляем сервисы
+ if (this._services) {
+ for (const p of Object.keys(this._services))
+ context.register(p, this._services[p]);
+ }
+
+ if (this._lazy) {
+ const saved = context.clone();
+
+ return (cfg: ServiceMap) => {
+ // защищаем контекст на случай исключения в процессе
+ // активации
+ const ct = saved.clone();
+ try {
+ if (cfg) {
+ for (const k in cfg)
+ ct.register(k, cfg[k]);
+ }
+
+ return this._optional ? ct.resolve(this._name, this._default) : ct
+ .resolve(this._name);
+ } catch (error) {
+ throw new ActivationError(this._name, ct.getStack(), error);
+ }
+ };
+ } else {
+ // добавляем сервисы
+ if (this._services) {
+ for (const p of Object.keys(this._services))
+ context.register(p, this._services[p]);
+ }
+
+ const v = this._optional ?
+ context.resolve(this._name, this._default) :
+ context.resolve(this._name);
+
+ return v;
+ }
+ }
+
+ toString() {
+ const opts = [];
+ if (this._optional)
+ opts.push("optional");
+ if (this._lazy)
+ opts.push("lazy");
+
+ const parts = [
+ "@ref "
+ ];
+ if (opts.length) {
+ parts.push("{");
+ parts.push(opts.join());
+ parts.push("} ");
+ }
+
+ parts.push(this._name);
+
+ if (!isNull(this._default)) {
+ parts.push(" = ");
+ parts.push(this._default);
+ }
+
+ return parts.join("");
+ }
+}
diff --git a/src/main/ts/di/ServiceDescriptor.ts b/src/main/ts/di/ServiceDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ServiceDescriptor.ts
@@ -0,0 +1,234 @@
+import { ActivationContext } from "./ActivationContext";
+import { Descriptor, ActivationType, ServiceMap, isDescriptor } from "./interfaces";
+import { Container } from "./Container";
+import { argumentNotNull, isPrimitive } from "../safe";
+import { TraceSource } from "../log/TraceSource";
+
+let cacheId = 0;
+
+const trace = TraceSource.get("@implab/core/di/ActivationContext");
+
+function injectMethod(target, method, context, args) {
+ const m = target[method];
+ if (!m)
+ throw new Error("Method '" + method + "' not found");
+
+ if (args instanceof Array)
+ return m.apply(target, context.parse(args, "." + method));
+ else
+ return m.call(target, context.parse(args, "." + method));
+}
+
+function makeClenupCallback(target, method: ((instance) => void) | string) {
+ if (typeof (method) === "string") {
+ return () => {
+ target[method]();
+ };
+ } else {
+ return () => {
+ method(target);
+ };
+ }
+}
+
+// TODO: make async
+function _parse(value, context: ActivationContext, path: string) {
+ if (isPrimitive(value))
+ return value;
+
+ trace.debug("parse {0}", path);
+
+ if (isDescriptor(value))
+ return context.activate(value, path);
+
+ if (value instanceof Array)
+ return value.map((x, i) => _parse(x, context, `${path}[${i}]`));
+
+ const t = {};
+ for (const p of Object.keys(value))
+ t[p] = _parse(value[p], context, `${path}.${p}`);
+
+ return t;
+}
+
+export interface ServiceDescriptorParams {
+ activation?: ActivationType;
+
+ owner: Container;
+
+ params?;
+
+ inject?: object[];
+
+ services?: ServiceMap;
+
+ cleanup?: ((x) => void) | string;
+}
+
+export class ServiceDescriptor implements Descriptor {
+ _instance;
+
+ _hasInstance = false;
+
+ _activationType = ActivationType.Call;
+
+ _services: ServiceMap;
+
+ _params;
+
+ _inject: object[];
+
+ _cleanup: ((x) => void) | string;
+
+ _cacheId: any;
+
+ _owner: Container;
+
+ constructor(opts: ServiceDescriptorParams) {
+ argumentNotNull(opts, "opts");
+ argumentNotNull(opts.owner, "owner");
+
+ this._owner = opts.owner;
+
+ if (opts.activation)
+ this._activationType = opts.activation;
+
+ if (opts.params)
+ this._params = opts.params;
+
+ if (opts.inject)
+ this._inject = opts.inject;
+
+ if (opts.services)
+ this._services = opts.services;
+
+ if (opts.cleanup) {
+ if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
+ throw new Error(
+ "The cleanup parameter must be either a function or a function name");
+
+ this._cleanup = opts.cleanup;
+ }
+ }
+
+ activate(context: ActivationContext) {
+ // if we have a local service records, register them first
+ let instance;
+
+ // ensure we have a cache id
+ if (!this._cacheId)
+ this._cacheId = ++cacheId;
+
+ switch (this._activationType) {
+ case ActivationType.Singleton: // SINGLETON
+ // if the value is cached return it
+ if (this._hasInstance)
+ return this._instance;
+
+ // singletons are bound to the root container
+ const container = context.container.getRootContainer();
+
+ if (container.has(this._cacheId)) {
+ instance = container.get(this._cacheId);
+ } else {
+ instance = this._create(context);
+ container.store(this._cacheId, instance);
+ if (this._cleanup)
+ container.onDispose(
+ makeClenupCallback(instance, this._cleanup));
+ }
+
+ this._hasInstance = true;
+ return (this._instance = instance);
+
+ case ActivationType.Container: // CONTAINER
+ // return a cached value
+
+ if (this._hasInstance)
+ return this._instance;
+
+ // create an instance
+ instance = this._create(context);
+
+ // the instance is bound to the container
+ if (this._cleanup)
+ this._owner.onDispose(
+ makeClenupCallback(instance, this._cleanup));
+
+ // cache and return the instance
+ this._hasInstance = true;
+ return (this._instance = instance);
+ case ActivationType.Context: // CONTEXT
+ // return a cached value if one exists
+
+ if (context.has(this._cacheId))
+ return context.get(this._cacheId);
+ // context context activated instances are controlled by callers
+ return context.store(this._cacheId, this._create(context));
+ case ActivationType.Call: // CALL
+ // per-call created instances are controlled by callers
+ return this._create(context);
+ case ActivationType.Hierarchy: // HIERARCHY
+ // hierarchy activated instances are behave much like container activated
+ // except they are created and bound to the child container
+
+ // return a cached value
+ if (context.container.has(this._cacheId))
+ return context.container.get(this._cacheId);
+
+ instance = this._create(context);
+
+ if (this._cleanup)
+ context.container.onDispose(makeClenupCallback(
+ instance,
+ this._cleanup));
+
+ return context.container.store(this._cacheId, instance);
+ default:
+ throw new Error("Invalid activation type: " + this._activationType);
+ }
+ }
+
+ isInstanceCreated() {
+ return this._hasInstance;
+ }
+
+ getInstance() {
+ return this._instance;
+ }
+
+ _factory(...params: any[]): any {
+ throw Error("Not implemented");
+ }
+
+ _create(context: ActivationContext) {
+ trace.debug(`constructing ${context._name}`);
+
+ if (this._activationType !== ActivationType.Call &&
+ context.visit(this._cacheId) > 0)
+ throw new Error("Recursion detected");
+
+ if (this._services) {
+ for (const p in this._services)
+ context.register(p, this._services[p]);
+ }
+
+ let instance;
+
+ if (this._params === undefined) {
+ instance = this._factory();
+ } else if (this._params instanceof Array) {
+ instance = this._factory.apply(this, _parse(this._params, context, "args"));
+ } else {
+ instance = this._factory(_parse(this._params, context, "args"));
+ }
+
+ if (this._inject) {
+ this._inject.forEach(spec => {
+ for (const m in spec)
+ injectMethod(instance, m, context, spec[m]);
+ });
+ }
+ return instance;
+ }
+}
diff --git a/src/main/ts/di/TypeServiceDescriptor.ts b/src/main/ts/di/TypeServiceDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/TypeServiceDescriptor.ts
@@ -0,0 +1,42 @@
+import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
+import { Constructor, Factory } from "../interfaces";
+import { argumentNotNull, isPrimitive } from "../safe";
+
+export interface TypeServiceDescriptorParams extends ServiceDescriptorParams {
+ type: Constructor;
+}
+
+export class TypeServiceDescriptor extends ServiceDescriptor {
+ _type: Constructor;
+
+ constructor(opts: TypeServiceDescriptorParams) {
+ super(opts);
+ argumentNotNull(opts && opts.type, "opts.type");
+
+ const ctor = this._type = opts.type;
+
+ if (this._params) {
+ if (this._params.length) {
+ this._factory = (...args) => {
+ const t = Object.create(ctor.prototype);
+ const inst = ctor.apply(t, args);
+ return isPrimitive(inst) ? t : inst;
+ };
+ } else {
+ this._factory = arg => {
+ return new ctor(arg);
+ };
+ }
+ } else {
+ this._factory = () => {
+ return new ctor();
+ };
+ }
+
+ }
+
+ toString() {
+ // @constructor {singleton} foo/bar/Baz
+ return ``;
+ }
+}
diff --git a/src/main/ts/di/ValueDescriptor.ts b/src/main/ts/di/ValueDescriptor.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/ValueDescriptor.ts
@@ -0,0 +1,17 @@
+import { Descriptor } from "./interfaces";
+
+export class ValueDescriptor implements Descriptor {
+ _value;
+
+ constructor(value) {
+ this._value = value;
+ }
+
+ activate() {
+ return this._value;
+ }
+
+ toString() {
+ return `@type=${typeof this._value}`;
+ }
+}
diff --git a/src/main/ts/di/interfaces.ts b/src/main/ts/di/interfaces.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/di/interfaces.ts
@@ -0,0 +1,75 @@
+import { isNull, isPrimitive } from "../safe";
+import { ActivationContext } from "./ActivationContext";
+import { Constructor, Factory } from "../interfaces";
+
+export interface Descriptor {
+ activate(context: ActivationContext, name?: string);
+}
+
+export function isDescriptor(x): x is Descriptor {
+ return (!isPrimitive(x)) &&
+ (x.activate instanceof Function);
+}
+
+export interface ServiceMap {
+ [s: string]: Descriptor;
+}
+
+export enum ActivationType {
+ Singleton,
+ Container,
+ Hierarchy,
+ Context,
+ Call
+}
+
+export interface RegistrationWithServices {
+ services?: object;
+}
+
+export interface ServiceRegistration extends RegistrationWithServices {
+
+ activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
+
+ params?;
+
+ inject?: object | object[];
+
+ cleanup?: (instance) => void | string;
+}
+
+export interface TypeRegistration extends ServiceRegistration {
+ $type: string | Constructor;
+}
+
+export interface FactoryRegistration extends ServiceRegistration {
+ $factory: string | Factory;
+}
+
+export interface ValueRegistration {
+ $value;
+ parse?: boolean;
+}
+
+export interface DependencyRegistration extends RegistrationWithServices {
+ $dependency: string;
+ lazy?: boolean;
+ optional?: boolean;
+ default?;
+}
+
+export function isTypeRegistration(x): x is TypeRegistration {
+ return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
+}
+
+export function isFactoryRegistration(x): x is FactoryRegistration {
+ return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
+}
+
+export function isValueRegistration(x): x is ValueRegistration {
+ return (!isPrimitive(x)) && ("$value" in x);
+}
+
+export function isDependencyRegistration(x): x is DependencyRegistration {
+ return (!isPrimitive(x)) && ("$dependency" in x);
+}
diff --git a/src/ts/interfaces.ts b/src/main/ts/interfaces.ts
rename from src/ts/interfaces.ts
rename to src/main/ts/interfaces.ts
--- a/src/ts/interfaces.ts
+++ b/src/main/ts/interfaces.ts
@@ -1,3 +1,11 @@
+export type Constructor = new (...args: any[]) => T;
+
+export type Factory = (...args: any[]) => T;
+
+export interface MapOf {
+ [key: string]: T;
+}
+
export interface IDestroyable {
destroy();
}
@@ -22,18 +30,18 @@ export interface IActivatable {
* Starts the component activation
* @param ct cancellation token for this operation
*/
- activate(ct?: ICancellation) : Promise;
+ activate(ct?: ICancellation): Promise;
/**
* Starts the component deactivation
* @param ct cancellation token for this operation
*/
- deactivate(ct?: ICancellation) : Promise;
+ deactivate(ct?: ICancellation): Promise;
/**
* Sets the activation controller for this component
* @param controller The activation controller
- *
+ *
* Activation controller checks whether this component
* can be activated and manages the active state of the
* component
@@ -71,6 +79,6 @@ export interface ICancellable {
}
export interface IObservable {
- on(next: (x:T) => void, error?: (e:any) => void, complete?:() => void): IDestroyable;
- next(ct?: ICancellation) : Promise;
-}
\ No newline at end of file
+ on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
+ next(ct?: ICancellation): Promise;
+}
diff --git a/src/main/ts/log/Registry.ts b/src/main/ts/log/Registry.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/log/Registry.ts
@@ -0,0 +1,55 @@
+import { TraceSource } from "./TraceSource";
+import { argumentNotNull } from "../safe";
+import { IDestroyable } from "../interfaces";
+
+export class Registry {
+ static readonly instance = new Registry();
+
+ private _registry: object = new Object();
+ private _listeners: object = new Object();
+ private _nextCookie: number = 1;
+
+ get(id: any): TraceSource {
+ argumentNotNull(id, "id");
+
+ if (this._registry[id])
+ return this._registry[id];
+
+ const source = new TraceSource(id);
+ this._registry[id] = source;
+ this._onNewSource(source);
+
+ return source;
+ }
+
+ add(id: any, source: TraceSource) {
+ argumentNotNull(id, "id");
+ argumentNotNull(source, "source");
+
+ this._registry[id] = source;
+ this._onNewSource(source);
+ }
+
+ _onNewSource(source: TraceSource) {
+ for (const i in this._listeners)
+ this._listeners[i].call(null, source);
+ }
+
+ on(handler: (source: TraceSource) => void): IDestroyable {
+ argumentNotNull(handler, "handler");
+ const me = this;
+
+ const cookie = this._nextCookie++;
+
+ this._listeners[cookie] = handler;
+
+ for (const i in this._registry)
+ handler(this._registry[i]);
+
+ return {
+ destroy() {
+ delete me._listeners[cookie];
+ }
+ };
+ }
+}
diff --git a/src/ts/log/TraceSource.ts b/src/main/ts/log/TraceSource.ts
rename from src/ts/log/TraceSource.ts
rename to src/main/ts/log/TraceSource.ts
--- a/src/ts/log/TraceSource.ts
+++ b/src/main/ts/log/TraceSource.ts
@@ -1,7 +1,6 @@
-import * as format from '../text/format'
-import { argumentNotNull } from '../safe';
-import { Observable } from '../Observable'
-import { IDestroyable } from '../interfaces';
+import { Observable } from "../Observable";
+import { Registry } from "./Registry";
+import { format } from "../text/StringFormat";
export const DebugLevel = 400;
@@ -13,91 +12,33 @@ export const ErrorLevel = 100;
export const SilentLevel = 0;
-export class TraceEvent {
+export interface TraceEvent {
readonly source: TraceSource;
- readonly level: Number;
+ readonly level: number;
readonly arg: any;
-
- constructor(source: TraceSource, level: Number, arg: any) {
- this.source = source;
- this.level = level;
- this.arg = arg;
- }
-}
-
-class Registry {
- static readonly instance = new Registry();
-
- private _registry: object = new Object();
- private _listeners: object = new Object();
- private _nextCookie: number = 1;
-
- get(id: any): TraceSource {
- argumentNotNull(id, "id");
-
- if (this._registry[id])
- return this._registry[id];
-
- var source = new TraceSource(id);
- this._registry[id] = source;
- this._onNewSource(source);
-
- return source;
- }
-
- add(id: any, source: TraceSource) {
- argumentNotNull(id, "id");
- argumentNotNull(source, "source");
-
- this._registry[id] = source;
- this._onNewSource(source);
- }
-
- _onNewSource(source: TraceSource) {
- for (let i in this._listeners)
- this._listeners[i].call(null, source);
- }
-
- on(handler: (source: TraceSource) => void): IDestroyable {
- argumentNotNull(handler, "handler");
- var me = this;
-
- var cookie = this._nextCookie++;
-
- this._listeners[cookie] = handler;
-
- for (let i in this._registry)
- handler(this._registry[i]);
-
- return {
- destroy() {
- delete me._listeners[cookie];
- }
- };
- }
}
export class TraceSource {
- readonly id: any
+ readonly id: any;
- level: number
+ level: number;
- readonly events: Observable
+ readonly events: Observable;
- _notifyNext: (arg: TraceEvent) => void
+ _notifyNext: (arg: TraceEvent) => void;
constructor(id: any) {
this.id = id || new Object();
- this.events = new Observable((next) => {
+ this.events = new Observable(next => {
this._notifyNext = next;
- })
+ });
}
protected emit(level: number, arg: any) {
- this._notifyNext(new TraceEvent(this, level, arg));
+ this._notifyNext({ source: this, level, arg });
}
isDebugEnabled() {
@@ -106,7 +47,7 @@ export class TraceSource {
debug(msg: string, ...args: any[]) {
if (this.isEnabled(DebugLevel))
- this.emit(DebugLevel, format.apply(null, arguments));
+ this.emit(DebugLevel, format(msg, args));
}
isLogEnabled() {
@@ -115,7 +56,7 @@ export class TraceSource {
log(msg: string, ...args: any[]) {
if (this.isEnabled(LogLevel))
- this.emit(LogLevel, format.apply(null, arguments));
+ this.emit(LogLevel, format(msg, args));
}
isWarnEnabled() {
@@ -124,7 +65,7 @@ export class TraceSource {
warn(msg: string, ...args: any[]) {
if (this.isEnabled(WarnLevel))
- this.emit(WarnLevel, format.apply(null, arguments));
+ this.emit(WarnLevel, format(msg, args));
}
/**
@@ -136,7 +77,7 @@ export class TraceSource {
/**
* Traces a error.
- *
+ *
* @param msg the message.
* @param args parameters which will be substituted in the message.
*/
@@ -148,7 +89,7 @@ export class TraceSource {
/**
* Checks whether the specified level is enabled for this
* trace source.
- *
+ *
* @param level the trace level which should be checked.
*/
isEnabled(level: number) {
@@ -157,7 +98,7 @@ export class TraceSource {
/**
* Traces a raw event, passing data as it is to the underlying listeners
- *
+ *
* @param level the level of the event
* @param arg the data of the event, can be a simple string or any object.
*/
@@ -169,7 +110,7 @@ export class TraceSource {
/**
* Register the specified handler to be called for every new and already
* created trace source.
- *
+ *
* @param handler the handler which will be called for each trace source
*/
static on(handler: (source: TraceSource) => void) {
@@ -178,11 +119,10 @@ export class TraceSource {
/**
* Creates or returns already created trace source for the specified id.
- *
+ *
* @param id the id for the trace source
*/
static get(id: any) {
return Registry.instance.get(id);
}
}
-
diff --git a/src/ts/log/writers/ConsoleWriter.ts b/src/main/ts/log/writers/ConsoleWriter.ts
rename from src/ts/log/writers/ConsoleWriter.ts
rename to src/main/ts/log/writers/ConsoleWriter.ts
--- a/src/ts/log/writers/ConsoleWriter.ts
+++ b/src/main/ts/log/writers/ConsoleWriter.ts
@@ -1,12 +1,13 @@
import { IObservable, IDestroyable, ICancellation } from "../../interfaces";
+import { TraceEvent, LogLevel, WarnLevel, DebugLevel } from "../TraceSource";
import { Cancellation } from "../../Cancellation";
-import { TraceEvent, LogLevel, WarnLevel } from "../TraceSource";
+import { destroy } from "../../safe";
export class ConsoleWriter implements IDestroyable {
readonly _subscriptions = new Array();
writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) {
- var subscription = source.on(this.writeEvent.bind(this));
+ const subscription = source.on(this.writeEvent.bind(this));
if (ct.isSupported()) {
ct.register(subscription.destroy.bind(subscription));
}
@@ -14,16 +15,22 @@ export class ConsoleWriter implements ID
}
writeEvent(next: TraceEvent) {
- if (next.level >= LogLevel) {
+ if (next.level >= DebugLevel) {
+ // tslint:disable-next-line
+ console.debug(next.source.id.toString(), next.arg);
+ } else if (next.level >= LogLevel) {
+ // tslint:disable-next-line
console.log(next.source.id.toString(), next.arg);
- } else if(next.level >= WarnLevel) {
+ } else if (next.level >= WarnLevel) {
+ // tslint:disable-next-line
console.warn(next.source.id.toString(), next.arg);
} else {
+ // tslint:disable-next-line
console.error(next.source.id.toString(), next.arg);
}
}
destroy() {
- this._subscriptions.forEach(x => x.destroy());
+ this._subscriptions.forEach(destroy);
}
-}
\ No newline at end of file
+}
diff --git a/src/ts/main.ts b/src/main/ts/main.ts
rename from src/ts/main.ts
rename to src/main/ts/main.ts
diff --git a/src/ts/safe.ts b/src/main/ts/safe.ts
rename from src/ts/safe.ts
rename to src/main/ts/safe.ts
--- a/src/ts/safe.ts
+++ b/src/main/ts/safe.ts
@@ -1,3 +1,18 @@
+let _nextOid = 0;
+const _oid = typeof Symbol === "function" ?
+ Symbol("__implab__oid__") :
+ "__implab__oid__";
+
+export function oid(instance: object): string {
+ if (isNull(instance))
+ return null;
+
+ if (_oid in instance)
+ return instance[_oid];
+ else
+ return (instance[_oid] = "oid_" + (++_nextOid));
+}
+
export function argumentNotNull(arg, name) {
if (arg === null || arg === undefined)
throw new Error("The argument " + name + " can't be null or undefined");
@@ -28,32 +43,53 @@ export function isPrimitive(arg) {
}
export function isInteger(arg) {
- return parseInt(arg) == arg;
+ return parseInt(arg, 10) === arg;
}
export function isNumber(arg) {
- return parseFloat(arg) == arg;
+ return parseFloat(arg) === arg;
}
export function isString(val) {
- return typeof (val) == "string" || val instanceof String;
+ return typeof (val) === "string" || val instanceof String;
+}
+
+export function isPromise(val): val is PromiseLike {
+ return "then" in val && val.then instanceof Function;
}
export function isNullOrEmptyString(str) {
if (str === null || str === undefined ||
- ((typeof (str) == "string" || str instanceof String) && str.length === 0))
+ ((typeof (str) === "string" || str instanceof String) && str.length === 0))
return true;
}
-export function isNotEmptyArray(arg) {
+export function isNotEmptyArray(arg): arg is Array {
return (arg instanceof Array && arg.length > 0);
}
+export function getGlobal() {
+ return this;
+}
+
+export function get(member: string, context?: object) {
+ argumentNotEmptyString(member, "member");
+ let that = context || getGlobal();
+ const parts = member.split(".");
+ for (const m of parts) {
+ if (!m)
+ continue;
+ if (isNull(that = that[m]))
+ break;
+ }
+ return that;
+}
+
/**
* Выполняет метод для каждого элемента массива, останавливается, когда
* либо достигнут конец массива, либо функция cb вернула
* значение.
- *
+ *
* @param {Array | Object} obj массив элементов для просмотра
* @param {Function} cb функция, вызываемая для каждого элемента
* @param {Object} thisArg значение, которое будет передано в качестве
@@ -61,43 +97,81 @@ export function isNotEmptyArray(arg) {
* @returns Результат вызова функции cb, либо undefined
* если достигнут конец массива.
*/
-export function each(obj, cb, thisArg) {
+export function each(obj, cb, thisArg?) {
argumentNotNull(cb, "cb");
- var i, x;
if (obj instanceof Array) {
- for (i = 0; i < obj.length; i++) {
- x = cb.call(thisArg, obj[i], i);
+ for (let i = 0; i < obj.length; i++) {
+ const x = cb.call(thisArg, obj[i], i);
if (x !== undefined)
return x;
}
} else {
- var keys = Object.keys(obj);
- for (i = 0; i < keys.length; i++) {
- var k = keys[i];
- x = cb.call(thisArg, obj[k], k);
+ const keys = Object.keys(obj);
+ for (const k of keys) {
+ const x = cb.call(thisArg, obj[k], k);
if (x !== undefined)
return x;
}
}
}
+/** Copies property values from a source object to the destination and returns
+ * the destination onject.
+ *
+ * @param dest The destination object into which properties from the source
+ * object will be copied.
+ * @param source The source of values which will be copied to the destination
+ * object.
+ * @param template An optional parameter specifies which properties should be
+ * copied from the source and how to map them to the destination. If the
+ * template is an array it contains the list of property names to copy from the
+ * source to the destination. In case of object the templates contains the map
+ * where keys are property names in the source and the values are property
+ * names in the destination object. If the template isn't specified then the
+ * own properties of the source are entirely copied to the destination.
+ *
+ */
+export function mixin(dest: T, source: S, template?: string[] | object): T & S {
+ argumentNotNull(dest, "to");
+ const _res = dest as T & S;
+
+ if (template instanceof Array) {
+ for (const p of template) {
+ if (p in source)
+ _res[p] = source[p];
+ }
+ } else if (template) {
+ const keys = Object.keys(source);
+ for (const p of keys) {
+ if (p in template)
+ _res[template[p]] = source[p];
+ }
+ } else {
+ const keys = Object.keys(source);
+ for (const p of keys)
+ _res[p] = source[p];
+ }
+
+ return _res;
+}
+
/** Wraps the specified function to emulate an asynchronous execution.
* @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
* @param{Function|String} fn [Required] Function wich will be wrapped.
*/
-export function async(_fn: (...args: any[]) => any, thisArg) : (...args: any[]) => PromiseLike {
+export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike {
let fn = _fn;
- if (arguments.length == 2 && !(fn instanceof Function))
+ if (arguments.length === 2 && !(fn instanceof Function))
fn = thisArg[fn];
if (fn == null)
throw new Error("The function must be specified");
- function wrapresult(x, e?) : PromiseLike {
+ function wrapresult(x, e?): PromiseLike {
if (e) {
return {
- then: function (cb, eb) {
+ then(cb, eb) {
try {
return eb ? wrapresult(eb(e)) : this;
} catch (e2) {
@@ -109,7 +183,7 @@ export function async(_fn: (...args: any
if (x && x.then)
return x;
return {
- then: function (cb) {
+ then(cb) {
try {
return cb ? wrapresult(cb(x)) : this;
} catch (e2) {
@@ -120,58 +194,58 @@ export function async(_fn: (...args: any
}
}
- return function () {
+ return (...args) => {
try {
- return wrapresult(fn.apply(thisArg, arguments));
+ return wrapresult(fn.apply(thisArg, args));
} catch (e) {
return wrapresult(null, e);
}
};
}
-export function delegate(target, _method: (string | Function)) {
- let method : Function;
+type _AnyFn = (...args) => any;
+
+export function delegate(target: T, _method: (K | _AnyFn)) {
+ let method;
if (!(_method instanceof Function)) {
argumentNotNull(target, "target");
method = target[_method];
+ if (!(method instanceof Function))
+ throw new Error("'method' argument must be a Function or a method name");
} else {
method = _method;
}
- if (!(method instanceof Function))
- throw new Error("'method' argument must be a Function or a method name");
-
- return function () {
- return method.apply(target, arguments);
+ return (...args) => {
+ return method.apply(target, args);
};
}
/**
* Для каждого элемента массива вызывает указанную функцию и сохраняет
* возвращенное значение в массиве результатов.
- *
+ *
* @remarks cb может выполняться асинхронно, при этом одновременно будет
* только одна операция.
- *
+ *
* @async
*/
export function pmap(items, cb) {
argumentNotNull(cb, "cb");
- if (items && items.then instanceof Function)
- return items.then(function (data) {
- return pmap(data, cb);
- });
+ if (isPromise(items))
+ return items.then(data => pmap(data, cb));
if (isNull(items) || !items.length)
return items;
- var i = 0,
- result = [];
+ let i = 0;
+ const result = [];
function next() {
- var r, ri;
+ let r;
+ let ri;
function chain(x) {
result[ri] = x;
@@ -182,7 +256,7 @@ export function pmap(items, cb) {
r = cb(items[i], i);
ri = i;
i++;
- if (r && r.then) {
+ if (isPromise(r)) {
return r.then(chain);
} else {
result[ri] = r;
@@ -197,22 +271,20 @@ export function pmap(items, cb) {
/**
* Выбирает первый элемент из последовательности, или обещания, если в
* качестве параметра используется обещание, оно должно вернуть массив.
- *
+ *
* @param {Function} cb обработчик результата, ему будет передан первый
* элемент последовательности в случае успеха
* @param {Function} err обработчик исключения, если массив пустой, либо
* не массив
- *
+ *
* @remarks Если не указаны ни cb ни err, тогда функция вернет либо
* обещание, либо первый элемент.
* @async
*/
-export function first(sequence: any, cb: Function, err: Function) {
+export function first(sequence, cb: (x) => any, err: (x) => any) {
if (sequence) {
- if (sequence.then instanceof Function) {
- return sequence.then(function (res) {
- return first(res, cb, err);
- }, err);
+ if (isPromise(sequence)) {
+ return sequence.then(res => first(res, cb, err));
} else if (sequence && "length" in sequence) {
if (sequence.length === 0) {
if (err)
@@ -230,7 +302,7 @@ export function first(sequence: any, cb:
throw new Error("The sequence is required");
}
-export function destroy(d: any) {
- if (d && 'destroy' in d)
+export function destroy(d) {
+ if (d && "destroy" in d)
d.destroy();
-}
\ No newline at end of file
+}
diff --git a/src/main/ts/text/StringFormat.ts b/src/main/ts/text/StringFormat.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/text/StringFormat.ts
@@ -0,0 +1,173 @@
+import { isPrimitive, isNull, each } from "../safe";
+import { MapOf } from "../interfaces";
+
+type SubstFn = (name: string, format?: string) => string;
+type TemplateFn = (subst: SubstFn) => string;
+type ConvertFn = (value: any, format?: string) => string;
+
+const map = {
+ "\\{": "&curlopen;",
+ "\\}": "&curlclose;",
+ "&": "&",
+ "\\:": ":"
+};
+
+const rev = {
+ curlopen: "{",
+ curlclose: "}",
+ amp: "&",
+ colon: ":"
+};
+
+function espaceString(s: string) {
+ if (!s)
+ return s;
+ return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'";
+}
+
+function encode(s: string) {
+ if (!s)
+ return s;
+ return s.replace(/\\{|\\}|&|\\:|\n/g, m => map[m] || m);
+}
+
+function decode(s: string) {
+ if (!s)
+ return s;
+ return s.replace(/&(\w+);/g, (m, $1) => rev[$1] || m);
+}
+
+function subst(s: string) {
+ const i = s.indexOf(":");
+ let name: string;
+ let pattern: string;
+ if (i >= 0) {
+ name = s.substr(0, i);
+ pattern = s.substr(i + 1);
+ } else {
+ name = s;
+ }
+
+ if (pattern)
+ return [
+ espaceString(decode(name)),
+ espaceString(decode(pattern))];
+ else
+ return [espaceString(decode(name))];
+}
+
+function _compile(str: string) {
+ if (!str)
+ return () => void 0;
+
+ const chunks = encode(str).split("{");
+ let chunk: string;
+
+ const code = ["var result=[];"];
+
+ for (let i = 0; i < chunks.length; i++) {
+ chunk = chunks[i];
+
+ if (i === 0) {
+ if (chunk)
+ code.push("result.push(" + espaceString(decode(chunk)) +
+ ");");
+ } else {
+ const len = chunk.indexOf("}");
+ if (len < 0)
+ throw new Error("Unbalanced substitution #" + i);
+
+ code.push("result.push(subst(" +
+ subst(chunk.substr(0, len)).join(",") + "));");
+ if (chunk.length > len + 1)
+ code.push("result.push(" +
+ espaceString(decode(chunk.substr(len + 1))) + ");");
+ }
+ }
+
+ code.push("return result.join('');");
+
+ // the code for this function is generated from the template
+ // tslint:disable-next-line:function-constructor
+ return new Function("subst", code.join("\n")) as TemplateFn;
+}
+
+const cache: MapOf = {};
+
+export function compile(template: string) {
+ let compiled = cache[template];
+ if (!compiled) {
+ compiled = _compile(template);
+ cache[template] = compiled;
+ }
+ return compiled;
+}
+
+function defaultConverter(value: any, pattern: string) {
+ if (pattern && pattern.toLocaleLowerCase() === "json") {
+ const seen = [];
+ return JSON.stringify(value, (k, v) => {
+ if (!isPrimitive(v)) {
+ const id = seen.indexOf(v);
+ if (id >= 0)
+ return "@ref-" + id;
+ else {
+ seen.push(v);
+ return v;
+ }
+ } else {
+ return v;
+ }
+ }, 2);
+ } else if (isNull(value)) {
+ return "";
+ } else if (value instanceof Date) {
+ return value.toISOString();
+ } else {
+ return pattern ? value.toString(pattern) : value.toString();
+ }
+}
+
+export class Formatter {
+ _converters: ConvertFn[];
+
+ constructor(converters?: ConvertFn[]) {
+ this._converters = converters || [];
+ this._converters.push(defaultConverter);
+ }
+
+ convert(value: any, pattern: string) {
+ for (const c of this._converters) {
+ const res = c(value, pattern);
+ if (!isNull(res))
+ return res;
+ }
+ return "";
+ }
+
+ format(msg: string, ...args: any[]) {
+ const template = compile(msg);
+
+ return template((name, pattern) => {
+ const value = args[name];
+ return !isNull(value) ? this.convert(value, pattern) : "";
+ });
+
+ }
+
+ compile(msg: string) {
+ const template = compile(msg);
+ return (...args: any[]) => {
+ return template((name, pattern) => {
+ const value = args[name];
+ return !isNull(value) ? this.convert(value, pattern) : "";
+ });
+ };
+ }
+}
+
+const _default = new Formatter();
+
+export function format(msg: string, ...args: any[]) {
+ return _default.format(msg, ...args);
+}
diff --git a/src/main/ts/text/TemplateCompiler.ts b/src/main/ts/text/TemplateCompiler.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/text/TemplateCompiler.ts
@@ -0,0 +1,102 @@
+import { format } from "./StringFormat";
+import { TraceSource, DebugLevel } from "../log/TraceSource";
+import { ITemplateParser, TokenType } from "./TemplateParser";
+
+const trace = TraceSource.get("@implab/text/TemplateCompiler");
+
+type TemplateFn = (obj: object) => string;
+
+export class TemplateCompiler {
+
+ _data: string[];
+ _code: string[];
+ _wrapWith = true;
+
+ constructor() {
+ this._code = [];
+ this._data = [];
+ }
+
+ compile(parser: ITemplateParser): TemplateFn {
+ this.preamble();
+ this.visitTemplate(parser);
+ this.postamble();
+
+ const text = this._code.join("\n");
+
+ try {
+ const compiled = new Function("obj, format, $data", text);
+ /**
+ * Функция форматирования по шаблону
+ *
+ * @type{Function}
+ * @param{Object} obj объект с параметрами для подстановки
+ */
+ return (obj: object) => compiled(obj || {}, format, this._data);
+ } catch (e) {
+ trace.traceEvent(DebugLevel, [e, text, this._data]);
+ throw e;
+ }
+ }
+
+ preamble() {
+ this._code.push(
+ "var $p = [];",
+ "var print = function(){",
+ " $p.push(format.apply(null,arguments));",
+ "};"
+ );
+
+ if (this._wrapWith)
+ this._code.push("with(obj){");
+ }
+
+ postamble() {
+ if (this._wrapWith)
+ this._code.push("}");
+
+ this._code.push("return $p.join('');");
+ }
+
+ visitTemplate(parser: ITemplateParser) {
+ while (parser.next()) {
+ switch (parser.token()) {
+ case TokenType.OpenBlock:
+ this.visitCode(parser);
+ break;
+ case TokenType.OpenInlineBlock:
+ this.visitInline(parser);
+ break;
+ default:
+ this.visitTextFragment(parser);
+ break;
+ }
+ }
+ }
+
+ visitInline(parser: ITemplateParser) {
+ const code = ["$p.push("];
+ while (parser.next()) {
+ if (parser.token() === TokenType.CloseBlock)
+ break;
+ code.push(parser.value());
+ }
+ code.push(");");
+ this._code.push(code.join(""));
+ }
+
+ visitCode(parser: ITemplateParser) {
+ const code = [];
+ while (parser.next()) {
+ if (parser.token() === TokenType.CloseBlock)
+ break;
+ code.push(parser.value());
+ }
+ this._code.push(code.join(""));
+ }
+
+ visitTextFragment(parser: ITemplateParser) {
+ const i = this._data.push(parser.value());
+ this._code.push("$p.push($data[" + i + "]);");
+ }
+}
diff --git a/src/main/ts/text/TemplateParser.ts b/src/main/ts/text/TemplateParser.ts
new file mode 100644
--- /dev/null
+++ b/src/main/ts/text/TemplateParser.ts
@@ -0,0 +1,64 @@
+import { argumentNotEmptyString } from "../safe";
+import { MapOf } from "../interfaces";
+
+const splitRx = /(<%=|\[%=|<%|\[%|%\]|%>)/;
+
+export enum TokenType {
+ None,
+ Text,
+ OpenInlineBlock,
+ OpenBlock,
+ CloseBlock
+}
+
+const tokenMap: MapOf = {
+ "<%": TokenType.OpenBlock,
+ "[%": TokenType.OpenBlock,
+ "<%=": TokenType.OpenInlineBlock,
+ "[%=": TokenType.OpenInlineBlock,
+ "%>": TokenType.CloseBlock,
+ "%]": TokenType.CloseBlock
+};
+
+export interface ITemplateParser {
+ next(): boolean;
+ token(): TokenType;
+ value(): string;
+}
+
+export class TemplateParser implements ITemplateParser {
+
+ _tokens: string[];
+ _pos = -1;
+ _type: TokenType;
+ _value: string;
+
+ constructor(text: string) {
+ argumentNotEmptyString(text, "text");
+
+ this._tokens = text.split(splitRx);
+ this._type = TokenType.None;
+ }
+
+ next() {
+ this._pos++;
+ if (this._pos < this._tokens.length) {
+ this._value = this._tokens[this._pos];
+ this._type = tokenMap[this._value] || TokenType.Text;
+ return true;
+ } else {
+ this._type = TokenType.None;
+ this._value = undefined;
+ return false;
+ }
+ }
+
+ token() {
+ return this._type;
+ }
+
+ value() {
+ return this._value;
+ }
+
+}
diff --git a/src/main/tsconfig.json b/src/main/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/src/main/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "rootDir": "ts"
+ },
+ "include": [
+ "ts/**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src/package.template.json b/src/package.template.json
new file mode 100644
--- /dev/null
+++ b/src/package.template.json
@@ -0,0 +1,23 @@
+{
+ "name": "${packageName}",
+ "version": "${version}",
+ "description": "${description}",
+ "main": "main.js",
+ "keywords": [
+ "di",
+ "ioc",
+ "logging",
+ "template engine",
+ "dependency injection"
+ ],
+ "author": "${author}",
+ "license": "${license}",
+ "repository": "$repository",
+ "publishConfig": {
+ "access": "public"
+ },
+ "peerDependencies": {
+ "dojo": "^1.10.0"
+ }
+ }
+
\ No newline at end of file
diff --git a/test/js/example.js b/src/test/js/example.js
rename from test/js/example.js
rename to src/test/js/example.js
diff --git a/src/test/js/mock/config1.js b/src/test/js/mock/config1.js
new file mode 100644
--- /dev/null
+++ b/src/test/js/mock/config1.js
@@ -0,0 +1,20 @@
+define({
+ foo: {
+ $type: "./Foo:Foo"
+ },
+
+ bar: {
+ $type: "./Bar:Bar",
+ params: {
+ db: {
+ provider: {
+ $dependency: "db"
+ }
+ },
+ foo: {
+ $type: "./Foo:Foo"
+ }
+ }
+ },
+ db: "db://localhost"
+});
\ No newline at end of file
diff --git a/test/js/plan.js b/src/test/js/plan.js
rename from test/js/plan.js
rename to src/test/js/plan.js
--- a/test/js/plan.js
+++ b/src/test/js/plan.js
@@ -1,3 +1,8 @@
-//define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]);
-//define(["./CancellationTests"]);
-define(["./ObservableTests"]);
\ No newline at end of file
+define([
+ "./ActivatableTests",
+ "./trace-test",
+ "./TraceSourceTests",
+ "./CancellationTests",
+ "./ObservableTests",
+ "./ContainerTests"
+]);
\ No newline at end of file
diff --git a/run-amd-tests.js b/src/test/js/run-amd-tests.js
rename from run-amd-tests.js
rename to src/test/js/run-amd-tests.js
--- a/run-amd-tests.js
+++ b/src/test/js/run-amd-tests.js
@@ -2,18 +2,13 @@ var requirejs = require('requirejs');
requirejs.config({
baseUrl: '.',
- map: {
- "*": {
- "@implab/core": "core"
- }
- },
packages: [{
- name: "core",
- location: "build/dist"
+ name: "@implab/core",
+ location: "build/dist/amd"
},
{
name: "test",
- location: "build/test"
+ location: "build/test/amd"
},
{
name: "dojo",
diff --git a/test/js/trace-test.js b/src/test/js/trace-test.js
rename from test/js/trace-test.js
rename to src/test/js/trace-test.js
--- a/test/js/trace-test.js
+++ b/src/test/js/trace-test.js
@@ -3,7 +3,7 @@ define(["tape"], function(tape) {
var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234';
var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b';
tape('Load TraceSource for the module', function(t) {
- require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource_1) {
+ require(["@implab/core/log/trace!" + sourceId, "@implab/core/log/TraceSource"], function(trace, TraceSource_1) {
var TraceSource = TraceSource_1.TraceSource;
t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter");
diff --git a/test/ts/ActivatableTests.ts b/src/test/ts/ActivatableTests.ts
rename from test/ts/ActivatableTests.ts
rename to src/test/ts/ActivatableTests.ts
--- a/test/ts/ActivatableTests.ts
+++ b/src/test/ts/ActivatableTests.ts
@@ -1,73 +1,25 @@
-import * as tape from 'tape';
-import { ActivatableMixin} from '@implab/core/components/ActivatableMixin';
-import { AsyncComponent } from '@implab/core/components/AsyncComponent';
-import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces';
-import { Cancellation } from '@implab/core/Cancellation';
-
-class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
-
-}
-
-class MockActivationController implements IActivationController {
-
- _active: IActivatable = null;
-
-
- getActive() : IActivatable {
- return this._active;
- }
+import * as tape from "tape";
+import { MockActivationController } from "./mock/MockActivationController";
+import { SimpleActivatable } from "./mock/SimpleActivatable";
- async deactivate() {
- if (this._active)
- await this._active.deactivate();
- this._active = null;
- }
-
- async activate(component: IActivatable) {
- if (!component || component.isActive())
- return;
- component.setActivationController(this);
-
- await component.activate();
- }
-
- async activating(component: IActivatable, ct: ICancellation = Cancellation.none) {
- if (component != this._active)
- await this.deactivate();
- }
+tape("simple activation", async t => {
- async activated(component: IActivatable, ct: ICancellation = Cancellation.none) {
- this._active = component;
- }
-
- async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) {
-
- }
+ const a = new SimpleActivatable();
+ t.false(a.isActive());
- async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) {
- if (this._active == component)
- this._active = null;
- }
-}
-
-tape('simple activation',async function(t){
-
- let a = new SimpleActivatable();
- t.false(a.isActive());
-
await a.activate();
t.true(a.isActive());
-
+
await a.deactivate();
t.false(a.isActive());
t.end();
});
-tape('controller activation', async function(t) {
+tape("controller activation", async t => {
- let a = new SimpleActivatable();
- let c = new MockActivationController();
+ const a = new SimpleActivatable();
+ const c = new MockActivationController();
t.false(a.isActive(), "the component is not active by default");
t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default");
@@ -89,11 +41,11 @@ tape('controller activation', async func
t.end();
});
-tape('handle error in onActivating', async function(t) {
- let a = new SimpleActivatable();
+tape("handle error in onActivating", async t => {
+ const a = new SimpleActivatable();
- a.onActivating = async function() {
- throw "Should fail";
+ a.onActivating = async () => {
+ throw new Error("Should fail");
};
try {
@@ -105,4 +57,4 @@ tape('handle error in onActivating', asy
t.false(a.isActive(), "the component should remain inactive");
t.end();
-});
\ No newline at end of file
+});
diff --git a/test/ts/CancellationTests.ts b/src/test/ts/CancellationTests.ts
rename from test/ts/CancellationTests.ts
rename to src/test/ts/CancellationTests.ts
--- a/test/ts/CancellationTests.ts
+++ b/src/test/ts/CancellationTests.ts
@@ -1,18 +1,18 @@
-import * as tape from 'tape';
-import { Cancellation } from '@implab/core/Cancellation';
-import { ICancellation } from '@implab/core/interfaces';
-import { delay } from './TestTraits';
+import * as tape from "tape";
+import { Cancellation } from "@implab/core/Cancellation";
+import { ICancellation } from "@implab/core/interfaces";
+import { delay } from "./TestTraits";
-tape('standalone cancellation', async t => {
+tape("standalone cancellation", async t => {
let doCancel: (e) => void;
- let ct = new Cancellation(cancel => {
+ const ct = new Cancellation(cancel => {
doCancel = cancel;
});
let counter = 0;
- let reason = "BILL";
+ const reason = "BILL";
t.true(ct.isSupported(), "Cancellation must be supported");
t.false(ct.isRequested(), "Cancellation shouldn't be requested");
@@ -33,7 +33,7 @@ tape('standalone cancellation', async t
t.equals(counter, 2, "The callback should be triggered immediately");
let msg;
- ct.register((e) => msg = e);
+ ct.register(e => msg = e);
t.equals(msg, reason, "The cancellation reason should be passed to callback");
try {
@@ -48,9 +48,9 @@ tape('standalone cancellation', async t
t.end();
});
-tape('async cancellation', async t => {
+tape("async cancellation", async t => {
- let ct = new Cancellation(cancel => {
+ const ct = new Cancellation(cancel => {
cancel("STOP!");
});
@@ -64,10 +64,10 @@ tape('async cancellation', async t => {
t.end();
});
-tape('cancel with external event', async t => {
- let ct = new Cancellation((cancel) => {
- setTimeout(x => cancel('STOP!'), 0);
- })
+tape("cancel with external event", async t => {
+ const ct = new Cancellation(cancel => {
+ setTimeout(x => cancel("STOP!"), 0);
+ });
try {
await delay(10000, ct);
@@ -79,10 +79,10 @@ tape('cancel with external event', async
t.end();
});
-tape('operation normal flow', async t => {
+tape("operation normal flow", async t => {
let htimeout;
- let ct = new Cancellation((cancel) => {
+ const ct = new Cancellation(cancel => {
htimeout = setTimeout(() => cancel("STOP!"), 1000);
});
@@ -94,4 +94,4 @@ tape('operation normal flow', async t =>
}
t.end();
-});
\ No newline at end of file
+});
diff --git a/src/test/ts/ContainerTests.ts b/src/test/ts/ContainerTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/ContainerTests.ts
@@ -0,0 +1,94 @@
+import { test, TapeWriter } from "./TestTraits";
+import { Container } from "@implab/core/di/Container";
+import { ReferenceDescriptor } from "@implab/core/di/ReferenceDescriptor";
+import { AggregateDescriptor } from "@implab/core/di/AggregateDescriptor";
+import { ValueDescriptor } from "@implab/core/di/ValueDescriptor";
+import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
+import { Foo } from "./mock/Foo";
+import { Bar } from "./mock/Bar";
+import { isNull } from "@implab/core/safe";
+
+test("Container register/resolve tests", async t => {
+ const container = new Container();
+
+ const connection1 = "db://localhost";
+
+ t.throws(
+ () => container.register("bla-bla", "bla-bla"),
+ "Do not allow to register anything other than descriptors"
+ );
+
+ t.doesNotThrow(
+ () => container.register("connection", new ValueDescriptor(connection1)),
+ "register ValueDescriptor"
+ );
+
+ t.equals(container.resolve("connection"), connection1, "resolve string value");
+
+ t.doesNotThrow(
+ () => container.register(
+ "dbParams",
+ new AggregateDescriptor({
+ timeout: 10,
+ connection: new ReferenceDescriptor({ name: "connection" })
+ })
+ ),
+ "register AggregateDescriptor"
+ );
+
+ const dbParams = container.resolve("dbParams");
+ t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
+});
+
+test("Container configure/resolve tests", async t => {
+
+ const container = new Container();
+
+ await container.configure({
+ foo: {
+ $type: Foo
+ },
+
+ box: {
+ $type: Bar,
+ params: {
+ $dependency: "foo"
+ }
+ },
+
+ bar: {
+ $type: Bar,
+ params: {
+ db: {
+ provider: {
+ $dependency: "db"
+ }
+ }
+ }
+ }
+ });
+ t.pass("should configure from js object");
+
+ const f1 = container.resolve("foo");
+
+ t.assert(!isNull(f1), "foo should be not null");
+
+ t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
+
+});
+
+test("Load configuration from module", async t => {
+ const container = new Container();
+
+ await container.configure("test/mock/config1");
+ t.pass("The configuration should load");
+
+ const f1 = container.resolve("foo");
+
+ t.assert(!isNull(f1), "foo should be not null");
+
+ const b1 = container.resolve("bar") as Bar;
+
+ t.assert(!isNull(b1), "bar should not be null");
+ t.assert(!isNull(b1.foo), "bar.foo should not be null");
+});
diff --git a/test/ts/ObservableTests.ts b/src/test/ts/ObservableTests.ts
rename from test/ts/ObservableTests.ts
rename to src/test/ts/ObservableTests.ts
--- a/test/ts/ObservableTests.ts
+++ b/src/test/ts/ObservableTests.ts
@@ -1,23 +1,22 @@
-import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource'
-import * as tape from 'tape';
-import { TapeWriter, delay } from './TestTraits';
-import { Observable } from '@implab/core/Observable';
-import { IObservable } from '@implab/core/interfaces';
-
-let trace = TraceSource.get("ObservableTests");
+import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
+import * as tape from "tape";
+import { TapeWriter, delay } from "./TestTraits";
+import { Observable } from "@implab/core/Observable";
+import { IObservable } from "@implab/core/interfaces";
-tape('events sequence example', async t => {
+const trace = TraceSource.get("ObservableTests");
+tape("events sequence example", async t => {
- let events: IObservable
+ let events: IObservable;
- let done = new Promise((resolve) => {
- events = new Observable(async (notify, fail, complete) => {
+ const done = new Promise(resolve => {
+ events = new Observable(async (notify, fail, finish) => {
for (let i = 0; i < 10; i++) {
await delay(0);
notify(i);
}
- complete();
+ finish();
resolve();
});
});
@@ -26,7 +25,7 @@ tape('events sequence example', async t
let complete = false;
events.on(x => count = count + x, null, () => complete = true);
- let first = await events.next();
+ const first = await events.next();
t.equals(first, 0, "the first event");
t.false(complete, "the sequence is not complete");
@@ -39,10 +38,10 @@ tape('events sequence example', async t
t.end();
});
-tape('event sequence termination', async t => {
- let events: IObservable
+tape("event sequence termination", async t => {
+ let events: IObservable;
- let done = new Promise((resolve) => {
+ const done = new Promise(resolve => {
events = new Observable(async (notify, fail, complete) => {
await delay(0);
notify(1);
@@ -55,14 +54,14 @@ tape('event sequence termination', async
});
let count = 0;
- events.on(() => {}, (e) => count++, () => count++);
+ events.on(() => {}, e => count++, () => count++);
- let first = await events.next();
+ const first = await events.next();
t.equals(first, 1, "the first message");
try {
await events.next();
t.fail("shoud throw an exception");
- } catch(e) {
+ } catch (e) {
t.pass("the sequence is terminated");
}
@@ -71,4 +70,4 @@ tape('event sequence termination', async
t.equals(count, 1, "the sequence must be terminated once");
t.end();
-});
\ No newline at end of file
+});
diff --git a/test/ts/TestTraits.ts b/src/test/ts/TestTraits.ts
rename from test/ts/TestTraits.ts
rename to src/test/ts/TestTraits.ts
--- a/test/ts/TestTraits.ts
+++ b/src/test/ts/TestTraits.ts
@@ -1,21 +1,21 @@
import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
import { Cancellation } from "@implab/core/Cancellation";
-import { TraceEvent, LogLevel, WarnLevel } from "@implab/core/log/TraceSource";
-import * as tape from 'tape';
+import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "@implab/core/log/TraceSource";
+import * as tape from "tape";
import { argumentNotNull } from "@implab/core/safe";
export class TapeWriter implements IDestroyable {
- readonly _tape: tape.Test
+ readonly _tape: tape.Test;
_subscriptions = new Array();
- constructor(tape: tape.Test) {
- argumentNotNull(tape, "tape");
- this._tape = tape;
+ constructor(t: tape.Test) {
+ argumentNotNull(t, "tape");
+ this._tape = t;
}
writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) {
- let subscription = source.on(this.writeEvent.bind(this));
+ const subscription = source.on(this.writeEvent.bind(this));
if (ct.isSupported()) {
ct.register(subscription.destroy.bind(subscription));
}
@@ -23,12 +23,14 @@ export class TapeWriter implements IDest
}
writeEvent(next: TraceEvent) {
- if (next.level >= LogLevel) {
- this._tape.comment("LOG " + next.arg);
+ if (next.level >= DebugLevel) {
+ this._tape.comment(`DEBUG ${next.source.id} ${next.arg}`);
+ } else if (next.level >= LogLevel) {
+ this._tape.comment(`LOG ${next.source.id} ${next.arg}`);
} else if (next.level >= WarnLevel) {
- this._tape.comment("WARN " + next.arg);
+ this._tape.comment(`WARN ${next.source.id} ${next.arg}`);
} else {
- this._tape.comment("ERROR " + next.arg);
+ this._tape.comment(`ERROR ${next.source.id} ${next.arg}`);
}
}
@@ -39,24 +41,49 @@ export class TapeWriter implements IDest
export async function delay(timeout: number, ct: ICancellation = Cancellation.none) {
let un: IDestroyable;
-
+
try {
- await new Promise((resolve, reject) => {
+ await new Promise((resolve, reject) => {
if (ct.isRequested()) {
- un = ct.register(reject);
- } else {
- let ht = setTimeout(() => {
- resolve();
- }, timeout);
-
- un = ct.register(e => {
- clearTimeout(ht);
- reject(e);
- });
- }
- });
+ un = ct.register(reject);
+ } else {
+ const ht = setTimeout(() => {
+ resolve();
+ }, timeout);
+
+ un = ct.register(e => {
+ clearTimeout(ht);
+ reject(e);
+ });
+ }
+ });
} finally {
- if(un)
- un.destroy();
- };
-}
\ No newline at end of file
+ if (un)
+ un.destroy();
+ }
+}
+
+export function test(name: string, cb: (t: tape.Test) => any) {
+ tape(name, async t => {
+ const writer = new TapeWriter(t);
+
+ TraceSource.on(ts => {
+ ts.level = DebugLevel;
+ writer.writeEvents(ts.events);
+ });
+
+ try {
+ await cb(t);
+ } catch (e) {
+
+ // verbose error information
+ // tslint:disable-next-line
+ console.error(e);
+ t.fail(e);
+
+ } finally {
+ t.end();
+ writer.destroy();
+ }
+ });
+}
diff --git a/test/ts/TraceSourceTests.ts b/src/test/ts/TraceSourceTests.ts
rename from test/ts/TraceSourceTests.ts
rename to src/test/ts/TraceSourceTests.ts
--- a/test/ts/TraceSourceTests.ts
+++ b/src/test/ts/TraceSourceTests.ts
@@ -1,15 +1,15 @@
-import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource'
-import * as tape from 'tape';
-import { TapeWriter } from './TestTraits';
+import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
+import * as tape from "tape";
+import { TapeWriter } from "./TestTraits";
-const sourceId = 'test/TraceSourceTests';
+const sourceId = "test/TraceSourceTests";
-tape('trace message', t => {
- let trace = TraceSource.get(sourceId);
+tape("trace message", t => {
+ const trace = TraceSource.get(sourceId);
trace.level = DebugLevel;
- let h = trace.events.on((ev) => {
+ const h = trace.events.on(ev => {
t.equal(ev.source, trace, "sender should be the current trace source");
t.equal(ev.level, DebugLevel, "level should be debug level");
t.equal(ev.arg, "Hello, World!", "The message should be a formatted message");
@@ -22,16 +22,16 @@ tape('trace message', t => {
h.destroy();
});
-tape('trace event', t => {
- let trace = TraceSource.get(sourceId);
+tape("trace event", t => {
+ const trace = TraceSource.get(sourceId);
trace.level = DebugLevel;
- let event = {
+ const event = {
name: "custom event"
};
- let h = trace.events.on((ev) => {
+ const h = trace.events.on(ev => {
t.equal(ev.source, trace, "sender should be the current trace source");
t.equal(ev.level, DebugLevel, "level should be debug level");
t.equal(ev.arg, event, "The message should be the specified object");
@@ -44,17 +44,17 @@ tape('trace event', t => {
h.destroy();
});
-tape('tape comment writer', async t => {
- let writer = new TapeWriter(t);
+tape("tape comment writer", async t => {
+ const writer = new TapeWriter(t);
TraceSource.on(ts => {
writer.writeEvents(ts.events);
});
- let trace = TraceSource.get(sourceId);
+ const trace = TraceSource.get(sourceId);
trace.level = DebugLevel;
- trace.log("Hello, {0}!", 'World');
+ trace.log("Hello, {0}!", "World");
trace.log("Multi\n line");
trace.warn("Look at me!");
trace.error("DIE!");
@@ -66,4 +66,4 @@ tape('tape comment writer', async t => {
t.comment("DONE");
t.end();
-});
\ No newline at end of file
+});
diff --git a/test/ts/dummy.ts b/src/test/ts/dummy.ts
rename from test/ts/dummy.ts
rename to src/test/ts/dummy.ts
--- a/test/ts/dummy.ts
+++ b/src/test/ts/dummy.ts
@@ -1,13 +1,13 @@
-import * as tape from 'tape';
-import * as uuid from '@implab/core/Uuid';
+import * as tape from "tape";
+import { Uuid } from "@implab/core/Uuid";
-tape('simple', function(t){
+tape("simple", t => {
t.pass("sync assert");
setTimeout(() => {
t.pass("async assert");
- t.comment(uuid());
- t.ok(uuid() != uuid());
+ t.comment(Uuid());
+ t.ok(Uuid() !== Uuid());
// end should be called after the last assertion
t.end();
}, 100);
-});
\ No newline at end of file
+});
diff --git a/src/test/ts/mock/Bar.ts b/src/test/ts/mock/Bar.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/mock/Bar.ts
@@ -0,0 +1,12 @@
+import { Foo } from "./Foo";
+
+export class Bar {
+ name = "bar";
+
+ foo: Foo;
+
+ constructor(_opts) {
+ if (_opts && _opts.foo)
+ this.foo = _opts.foo;
+ }
+}
diff --git a/src/test/ts/mock/Foo.ts b/src/test/ts/mock/Foo.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/mock/Foo.ts
@@ -0,0 +1,3 @@
+export class Foo {
+ name = "foo";
+}
diff --git a/src/test/ts/mock/MockActivationController.ts b/src/test/ts/mock/MockActivationController.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/mock/MockActivationController.ts
@@ -0,0 +1,43 @@
+import { IActivatable, ICancellation, IActivationController } from "@implab/core/interfaces";
+import { Cancellation } from "@implab/core/Cancellation";
+
+export class MockActivationController implements IActivationController {
+
+ _active: IActivatable = null;
+
+ getActive(): IActivatable {
+ return this._active;
+ }
+
+ async deactivate() {
+ if (this._active)
+ await this._active.deactivate();
+ this._active = null;
+ }
+
+ async activate(component: IActivatable) {
+ if (!component || component.isActive())
+ return;
+ component.setActivationController(this);
+
+ await component.activate();
+ }
+
+ async activating(component: IActivatable, ct: ICancellation = Cancellation.none) {
+ if (component !== this._active)
+ await this.deactivate();
+ }
+
+ async activated(component: IActivatable, ct: ICancellation = Cancellation.none) {
+ this._active = component;
+ }
+
+ async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) {
+
+ }
+
+ async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) {
+ if (this._active === component)
+ this._active = null;
+ }
+}
diff --git a/src/test/ts/mock/SimpleActivatable.ts b/src/test/ts/mock/SimpleActivatable.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/mock/SimpleActivatable.ts
@@ -0,0 +1,6 @@
+import { AsyncComponent } from "@implab/core/components/AsyncComponent";
+import { ActivatableMixin } from "@implab/core/components/ActivatableMixin";
+
+export class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
+
+}
diff --git a/src/test/tsconfig.json b/src/test/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/src/test/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "rootDir": "ts",
+ "baseUrl": ".",
+ "paths": {
+ "@implab/core/*": [
+ "../../build/dist/amd/*"
+ ]
+ }
+ },
+ "include" : [
+ "ts/**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/src/ts/text/format.d.ts b/src/ts/text/format.d.ts
deleted file mode 100644
--- a/src/ts/text/format.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-declare function format(format: string, ...args: any[]): string;
-
-declare namespace format {
-
-}
-
-export = format;
\ No newline at end of file
diff --git a/src/tsconfig.json b/src/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/src/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "es3",
+ "sourceMap": true,
+ "declaration": true,
+ "moduleResolution": "node",
+ "noEmitOnError": true,
+ "listFiles": true,
+ "lib": [
+ "es5",
+ "es2015.promise",
+ "es2015.symbol",
+ "dom"
+ ],
+ "types": []
+ },
+ "files": []
+}
\ No newline at end of file
diff --git a/tsc.json b/tsc.json
deleted file mode 100644
--- a/tsc.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "compilerOptions": {
- "target": "es5",
- "module": "amd",
- "sourceMap": true,
- "outDir" : "build/dist",
- "declaration": true,
- "lib": [
- "ES2015"
- ]
- },
- "include" : [
- "src/ts/**/*.ts"
- ]
-}
\ No newline at end of file
diff --git a/tsc.test.json b/tsc.test.json
deleted file mode 100644
--- a/tsc.test.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "compilerOptions": {
- "target": "es5",
- "module": "amd",
- "sourceMap": true,
- "outDir" : "build/test",
- "moduleResolution": "node",
- "lib": [
- "ES2015"
- ]
- },
- "include" : [
- "test/ts/**/*.ts"
- ]
-}
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
new file mode 100644
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,40 @@
+{
+ "extends": "tslint:recommended",
+ "rules": {
+ "align": [
+ true,
+ "parameters",
+ "statements"
+ ],
+ "interface-name": [false],
+ "max-line-length": [ true, 185 ],
+ "member-access": false,
+ "member-ordering": [
+ false,
+ "variables-before-functions"
+ ],
+ "no-bitwise": false,
+ "no-empty": false,
+ "no-namespace": false,
+ "no-string-literal": false,
+ "ordered-imports": false,
+ "one-line": [
+ true,
+ "check-open-brace",
+ "check-catch",
+ "check-whitespace"
+ ],
+ "object-literal-sort-keys": false,
+ "trailing-comma": [
+ true,
+ {
+ "singleline": "never",
+ "multiline": "never"
+ }
+ ],
+ "variable-name": false,
+ "curly": false,
+ "array-type": false,
+ "arrow-parens": [true, "ban-single-arg-parens"]
+ }
+}
\ No newline at end of file