Repository: ioBroker/create-adapter
Pull request #62 (2019-01-17 13:36:12)

Simplify templates
**PR Checklist:**  
- [x] Provide a meaningful description to this PR or mention which issues this fixes.
- [ ] ~~Add tests for your change. This includes negative tests (i.e. inputs that need to fail) as well as baseline tests (i.e. how should the directory structure look like?).~~ **not necessary**
- [x] Run the test suite with `npm test`
- [x] If there are baseline changes, review them and make a separate commit for them with the comment "accept baselines" if they are desired changes
- [x] Ensure the project builds with `npm run build` and include those artifacts aswell
- [x] Add your changes to `CHANGELOG.md` (referencing this PR or the issue you fixed)

**Description:**  
Moves the templates dir into the root folder.  
Simplifies how the templates are defined. Entirely static templates are now defined in separate files with the correct extension.




Patch:
diff --git a/.npmignore b/.npmignore
index 44d47bb..cc5458e 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,4 +1,4 @@
-.git*
+.*
 *.njsproj
 *.sln
 tsconfig.json
@@ -7,6 +7,7 @@ tslint.json
 test/
 tests/
 src/
+templates/
 build/**/*.map
 iobroker-create-adapter-*.tgz
 .vs*/
@@ -14,6 +15,3 @@ iobroker-create-adapter-*.tgz
 obj/
 coverage/
 maintenance/
-.travis.yml
-.circleci/
-.editorconfig
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a4dc5a..9a2f0ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 * (ldittmar) Add zh-cn (chinese) language
 * (jogibear9988) Add translation to gulp
 * (ldittbar) Improve README.md (#25)
+* (AlCalzone) Allow editing static templates in separate files with the correct extension (#62)
 
 ## 1.5.0 (2019-01-07)
 * (AlCalzone) Allow targeting newer ES version when using JS (fixes #43)
diff --git a/bin/create-adapter.js b/bin/create-adapter.js
index 89394a7..d374927 100644
--- a/bin/create-adapter.js
+++ b/bin/create-adapter.js
@@ -1,2 +1,2 @@
 #!/usr/bin/env node
-require("../build/cli.js");
+require("../build/src/cli.js");
diff --git a/build/cli.js b/build/src/cli.js
similarity index 100%
rename from build/cli.js
rename to build/src/cli.js
diff --git a/build/index.js b/build/src/index.js
similarity index 100%
rename from build/index.js
rename to build/src/index.js
diff --git a/build/lib/actionsAndTransformers.js b/build/src/lib/actionsAndTransformers.js
similarity index 100%
rename from build/lib/actionsAndTransformers.js
rename to build/src/lib/actionsAndTransformers.js
diff --git a/build/lib/createAdapter.js b/build/src/lib/createAdapter.js
similarity index 90%
rename from build/lib/createAdapter.js
rename to build/src/lib/createAdapter.js
index 6f97e1e..9a631f3 100644
--- a/build/lib/createAdapter.js
+++ b/build/src/lib/createAdapter.js
@@ -4,7 +4,7 @@ const typeguards_1 = require("alcalzone-shared/typeguards");
 const fs = require("fs-extra");
 const os = require("os");
 const path = require("path");
-const templateFiles = require("../templates");
+const templateFiles = require("../../templates");
 const tools_1 = require("./tools");
 function testCondition(condition, answers) {
     if (condition == undefined)
@@ -30,7 +30,7 @@ function testCondition(condition, answers) {
 }
 exports.testCondition = testCondition;
 async function createFiles(answers) {
-    const creatorVersion = require("../../package.json").version;
+    const creatorVersion = tools_1.getOwnVersion();
     const answersWithMeta = Object.assign({}, answers, { creatorVersion });
     const files = await Promise.all(templateFiles.map(async ({ name, templateFunction }) => {
         const customPath = typeof templateFunction.customPath === "function" ? templateFunction.customPath(answersWithMeta)
@@ -82,3 +82,11 @@ async function writeFiles(targetDir, files) {
     }
 }
 exports.writeFiles = writeFiles;
+async function readFile(file, relativeTo, binary = false) {
+    const absolutePath = path.join(relativeTo, file);
+    if (binary)
+        return fs.readFile(absolutePath);
+    else
+        return fs.readFile(absolutePath, "utf8");
+}
+exports.readFile = readFile;
diff --git a/build/lib/fetchVersions.js b/build/src/lib/fetchVersions.js
similarity index 100%
rename from build/lib/fetchVersions.js
rename to build/src/lib/fetchVersions.js
diff --git a/build/lib/licenses.js b/build/src/lib/licenses.js
similarity index 100%
rename from build/lib/licenses.js
rename to build/src/lib/licenses.js
diff --git a/build/lib/questions.js b/build/src/lib/questions.js
similarity index 99%
rename from build/lib/questions.js
rename to build/src/lib/questions.js
index 0109c6f..5b594a4 100644
--- a/build/lib/questions.js
+++ b/build/src/lib/questions.js
@@ -5,6 +5,7 @@ const ansi_colors_1 = require("ansi-colors");
 const actionsAndTransformers_1 = require("./actionsAndTransformers");
 const createAdapter_1 = require("./createAdapter");
 const licenses_1 = require("./licenses");
+const tools_1 = require("./tools");
 function isQuestionGroup(val) {
     if (val == undefined)
         return false;
@@ -28,13 +29,11 @@ function styledMultiselect(ms) {
         },
     });
 }
-// tslint:disable-next-line:no-var-requires
-const ownVersion = require("../../package.json").version;
 /** All questions and the corresponding text lines */
 exports.questionsAndText = [
     "",
     ansi_colors_1.green.bold("====================================================="),
-    ansi_colors_1.green.bold(`   Welcome to the ioBroker adapter creator v${ownVersion}!`),
+    ansi_colors_1.green.bold(`   Welcome to the ioBroker adapter creator v${tools_1.getOwnVersion()}!`),
     ansi_colors_1.green.bold("====================================================="),
     "",
     ansi_colors_1.gray(`You can cancel at any point by pressing Ctrl+C.`),
diff --git a/build/lib/tools.js b/build/src/lib/tools.js
similarity index 96%
rename from build/lib/tools.js
rename to build/src/lib/tools.js
index 84989c2..178613e 100644
--- a/build/lib/tools.js
+++ b/build/src/lib/tools.js
@@ -235,3 +235,16 @@ exports.tsFixQuotes = tsFixQuotes;
 function escapeQuotes(str, newQuotes, oldQuotes) {
     return str.replace(new RegExp(newQuotes, "g"), `\\${newQuotes}`).replace(new RegExp(`\\\\${oldQuotes}`, "g"), oldQuotes);
 }
+function getOwnVersion() {
+    for (const jsonPath of [
+        "../../package.json",
+        "../../../package.json",
+    ]) {
+        try {
+            return require(jsonPath).version;
+        }
+        catch (e) { /* OK */ }
+    }
+    return "unknown";
+}
+exports.getOwnVersion = getOwnVersion;
diff --git a/build/templates/LICENSE.js b/build/templates/LICENSE.js
index e4b7803..2c25f70 100644
--- a/build/templates/LICENSE.js
+++ b/build/templates/LICENSE.js
@@ -1,5 +1,5 @@
 "use strict";
-const tools_1 = require("../lib/tools");
+const tools_1 = require("../src/lib/tools");
 module.exports = (answers => {
     return answers.license
         && answers.license.text
diff --git a/build/templates/README.md.js b/build/templates/README.md.js
index f6a8620..197721b 100644
--- a/build/templates/README.md.js
+++ b/build/templates/README.md.js
@@ -1,5 +1,5 @@
 "use strict";
-const tools_1 = require("../lib/tools");
+const tools_1 = require("../src/lib/tools");
 module.exports = (answers => {
     const adapterNameLowerCase = answers.adapterName.toLowerCase();
     const template = `
diff --git a/src/templates/_travis.yml.ts b/build/templates/_travis.raw.yml
similarity index 62%
rename from src/templates/_travis.yml.ts
rename to build/templates/_travis.raw.yml
index 8ab8016..3f99e77 100644
--- a/src/templates/_travis.yml.ts
+++ b/build/templates/_travis.raw.yml
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../lib/createAdapter";
-
-const templateFunction: TemplateFunction = answers => {
-
-	const isAdapter = answers.features.indexOf("adapter") > -1;
-	if (!isAdapter) return;
-
-	const template = `
 os:
   - linux
   - osx
@@ -38,11 +30,3 @@ before_script:
 script:
   - npm run test:package
   - npm run test:iobroker
-`;
-	return template.trim();
-};
-
-templateFunction.customPath = ".travis.yml";
-// .travis.yml is always formatted with 2 spaces
-templateFunction.noReformat = true;
-export = templateFunction;
diff --git a/build/templates/_travis.yml.js b/build/templates/_travis.yml.js
index 4be64cd..0df7c9e 100644
--- a/build/templates/_travis.yml.js
+++ b/build/templates/_travis.yml.js
@@ -1,43 +1,10 @@
 "use strict";
+const createAdapter_1 = require("../src/lib/createAdapter");
 const templateFunction = answers => {
     const isAdapter = answers.features.indexOf("adapter") > -1;
     if (!isAdapter)
         return;
-    const template = `
-os:
-  - linux
-  - osx
-  - windows
-
-language: node_js
-node_js:
-  - '6'
-  - '8'
-  - '10'
-
-env:
-  - CXX=g++-4.8
-addons:
-  apt:
-    sources:
-      - ubuntu-toolchain-r-test
-    packages:
-      - g++-4.9
-
-before_install:
-  - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then CC=gcc-4.9; fi'
-  - 'if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then CC=g++-4.9; fi'
-before_script:
-  - export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
-  - 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm; fi'
-  - npm -v
-  - npm install winston@2.3.1
-  - 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
-script:
-  - npm run test:package
-  - npm run test:iobroker
-`;
-    return template.trim();
+    return createAdapter_1.readFile("_travis.raw.yml", __dirname);
 };
 templateFunction.customPath = ".travis.yml";
 // .travis.yml is always formatted with 2 spaces
diff --git a/build/templates/admin/admin.d.ts.js b/build/templates/admin/admin.d.ts.js
index adfdc05..96c3986 100644
--- a/build/templates/admin/admin.d.ts.js
+++ b/build/templates/admin/admin.d.ts.js
@@ -1,11 +1,9 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
     if (!useTypeScript && !useTypeChecking)
         return;
-    const template = `
-declare let systemDictionary: Record<string, Record<string, string>>;
-`;
-    return template.trim();
+    return createAdapter_1.readFile("admin.raw.d.ts", __dirname);
 });
diff --git a/build/templates/admin/admin.raw.d.ts b/build/templates/admin/admin.raw.d.ts
new file mode 100644
index 0000000..13444d9
--- /dev/null
+++ b/build/templates/admin/admin.raw.d.ts
@@ -0,0 +1,1 @@
+declare let systemDictionary: Record<string, Record<string, string>>;
diff --git a/build/templates/admin/icon.png.js b/build/templates/admin/icon.png.js
index f4a77e7..1a5f6b2 100644
--- a/build/templates/admin/icon.png.js
+++ b/build/templates/admin/icon.png.js
@@ -1,7 +1,6 @@
 "use strict";
-const fs = require("fs-extra");
-const path = require("path");
-const templateFunction = () => fs.readFile(path.join(__dirname, "../../../adapter-creator.png"));
+const createAdapter_1 = require("../../src/lib/createAdapter");
+const templateFunction = () => createAdapter_1.readFile("../../adapter-creator.png", __dirname, true);
 templateFunction.customPath = answers => `admin/${answers.adapterName}.png`;
 templateFunction.noReformat = true; // Don't format binary files
 module.exports = templateFunction;
diff --git a/build/templates/admin/style.css.js b/build/templates/admin/style.css.js
index 0824f7f..5d15806 100644
--- a/build/templates/admin/style.css.js
+++ b/build/templates/admin/style.css.js
@@ -1,20 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const isAdapter = answers.features.indexOf("adapter") > -1;
     if (!isAdapter)
         return;
-    const template = `
-/* You can delete those if you want. I just found them very helpful */
-* {
-	box-sizing: border-box
-}
-.m {
-	/* Don't cut off dropdowns! */
-	overflow: initial;
-}
-
-/* Add your styles here */
-
-`;
-    return template.trim();
+    return createAdapter_1.readFile("style.raw.css", __dirname);
 });
diff --git a/build/templates/admin/style.raw.css b/build/templates/admin/style.raw.css
new file mode 100644
index 0000000..5d68855
--- /dev/null
+++ b/build/templates/admin/style.raw.css
@@ -0,0 +1,10 @@
+/* You can delete those if you want. I just found them very helpful */
+* {
+	box-sizing: border-box
+}
+.m {
+	/* Don't cut off dropdowns! */
+	overflow: initial;
+}
+
+/* Add your styles here */
diff --git a/build/templates/admin/tsconfig.json.js b/build/templates/admin/tsconfig.json.js
index d84510c..1f5ee41 100644
--- a/build/templates/admin/tsconfig.json.js
+++ b/build/templates/admin/tsconfig.json.js
@@ -1,17 +1,9 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
     if (!useTypeScript && !useTypeChecking)
         return;
-    const template = `
-{
-	"extends": "../tsconfig.json",
-	"include": [
-		"./**/*.d.ts",
-		"./**/*.js"
-	]
-}
-`;
-    return template.trim();
+    return createAdapter_1.readFile("tsconfig.raw.json", __dirname);
 });
diff --git a/build/templates/admin/tsconfig.raw.json b/build/templates/admin/tsconfig.raw.json
new file mode 100644
index 0000000..6dcddfa
--- /dev/null
+++ b/build/templates/admin/tsconfig.raw.json
@@ -0,0 +1,7 @@
+{
+	"extends": "../tsconfig.json",
+	"include": [
+		"./**/*.d.ts",
+		"./**/*.js"
+	]
+}
diff --git a/build/templates/io-package.json.js b/build/templates/io-package.json.js
index 9121112..646b2a4 100644
--- a/build/templates/io-package.json.js
+++ b/build/templates/io-package.json.js
@@ -1,7 +1,7 @@
 "use strict";
 const objects_1 = require("alcalzone-shared/objects");
 const JSON5 = require("json5");
-const tools_1 = require("../lib/tools");
+const tools_1 = require("../src/lib/tools");
 module.exports = (async (answers) => {
     const isAdapter = answers.features.indexOf("adapter") > -1;
     const isWidget = answers.features.indexOf("vis") > -1;
diff --git a/build/templates/lib/tools.js.js b/build/templates/lib/tools.js.js
index 7cb74ce..6fad049 100644
--- a/build/templates/lib/tools.js.js
+++ b/build/templates/lib/tools.js.js
@@ -1,61 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const useJavaScript = answers.language === "JavaScript";
     if (!useJavaScript)
         return;
-    const template = `
-const axios = require("axios");
-
-/**
- * Tests whether the given variable is a real object and not an Array
- * @param {any} it The variable to test
- * @returns {it is Record<string, any>}
- */
-function isObject(it) {
-	// This is necessary because:
-	// typeof null === 'object'
-	// typeof [] === 'object'
-	// [] instanceof Object === true
-	return Object.prototype.toString.call(it) === "[object Object]";
-}
-
-/**
- * Tests whether the given variable is really an Array
- * @param {any} it The variable to test
- * @returns {it is any[]}
- */
-function isArray(it) {
-	if (Array.isArray != null) return Array.isArray(it);
-	return Object.prototype.toString.call(it) === "[object Array]";
-}
-
-/**
- * Translates text using the Google Translate API
- * @param {string} text The text to translate
- * @param {string}targetLang The target languate
- * @returns {Promise<string>}
- */
-async function translateText(text, targetLang) {
-	if (targetLang === "en") return text;
-	try {
-		const url = \`http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=\${targetLang}&dt=t&q=\${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8\`;
-		const response = await axios({url, timeout: 5000});
-		if (isArray(response.data)) {
-			// we got a valid response
-			return response.data[0][0][0];
-		}
-		throw new Error("Invalid response for translate request");
-	} catch (e) {
-		throw new Error(\`Could not translate to "\${targetLang}": \${e}\`);
-	}
-	return text;
-}
-
-module.exports = {
-	isArray,
-	isObject,
-	translateText
-};
-`;
-    return template.trim();
+    return createAdapter_1.readFile("tools.raw.js", __dirname);
 });
diff --git a/src/templates/lib/tools.js.ts b/build/templates/lib/tools.raw.js
similarity index 72%
rename from src/templates/lib/tools.js.ts
rename to build/templates/lib/tools.raw.js
index 9023dd5..4f538ee 100644
--- a/src/templates/lib/tools.js.ts
+++ b/build/templates/lib/tools.raw.js
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../../lib/createAdapter";
-
-export = (answers => {
-
-	const useJavaScript = answers.language === "JavaScript";
-	if (!useJavaScript) return;
-
-	const template = `
 const axios = require("axios");
 
 /**
@@ -40,7 +32,7 @@ function isArray(it) {
 async function translateText(text, targetLang) {
 	if (targetLang === "en") return text;
 	try {
-		const url = \`http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=\${targetLang}&dt=t&q=\${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8\`;
+		const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
 		const response = await axios({url, timeout: 5000});
 		if (isArray(response.data)) {
 			// we got a valid response
@@ -48,7 +40,7 @@ async function translateText(text, targetLang) {
 		}
 		throw new Error("Invalid response for translate request");
 	} catch (e) {
-		throw new Error(\`Could not translate to "\${targetLang}": \${e}\`);
+		throw new Error(`Could not translate to "${targetLang}": ${e}`);
 	}
 	return text;
 }
@@ -58,6 +50,3 @@ module.exports = {
 	isObject,
 	translateText
 };
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/package.json.js b/build/templates/package.json.js
index b350d31..1b87882 100644
--- a/build/templates/package.json.js
+++ b/build/templates/package.json.js
@@ -1,7 +1,7 @@
 "use strict";
 const async_1 = require("alcalzone-shared/async");
 const JSON5 = require("json5");
-const fetchVersions_1 = require("../lib/fetchVersions");
+const fetchVersions_1 = require("../src/lib/fetchVersions");
 const templateFunction = async (answers) => {
     const isAdapter = answers.features.indexOf("adapter") > -1;
     const isWidget = answers.features.indexOf("vis") > -1;
diff --git a/src/templates/src/lib/tools.ts.ts b/build/templates/src/lib/tools.raw.ts
similarity index 70%
rename from src/templates/src/lib/tools.ts.ts
rename to build/templates/src/lib/tools.raw.ts
index d8548f0..796a52a 100644
--- a/src/templates/src/lib/tools.ts.ts
+++ b/build/templates/src/lib/tools.raw.ts
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../../../lib/createAdapter";
-
-export = (answers => {
-
-	const useTypeScript = answers.language === "TypeScript";
-	if (!useTypeScript) return;
-
-	const template = `
 import axios from "axios";
 
 /**
@@ -37,7 +29,7 @@ export function isArray(it: any): it is any[] {
 export async function translateText(text: string, targetLang: string): Promise<string> {
 	if (targetLang === "en") return text;
 	try {
-		const url = \`http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=\${targetLang}&dt=t&q=\${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8\`;
+		const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
 		const response = await axios({url, timeout: 5000});
 		if (isArray(response.data)) {
 			// we got a valid response
@@ -45,10 +37,7 @@ export async function translateText(text: string, targetLang: string): Promise<s
 		}
 		throw new Error("Invalid response for translate request");
 	} catch (e) {
-		throw new Error(\`Could not translate to "\${targetLang}": \${e}\`);
+		throw new Error(`Could not translate to "${targetLang}": ${e}`);
 	}
 	return text;
 }
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/src/lib/tools.ts.js b/build/templates/src/lib/tools.ts.js
index 2ddbb92..610ba2e 100644
--- a/build/templates/src/lib/tools.ts.js
+++ b/build/templates/src/lib/tools.ts.js
@@ -1,52 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../../../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     if (!useTypeScript)
         return;
-    const template = `
-import axios from "axios";
-
-/**
- * Tests whether the given variable is a real object and not an Array
- * @param it The variable to test
- */
-export function isObject(it: any): it is object {
-	// This is necessary because:
-	// typeof null === 'object'
-	// typeof [] === 'object'
-	// [] instanceof Object === true
-	return Object.prototype.toString.call(it) === "[object Object]";
-}
-
-/**
- * Tests whether the given variable is really an Array
- * @param it The variable to test
- */
-export function isArray(it: any): it is any[] {
-	if (Array.isArray != null) return Array.isArray(it);
-	return Object.prototype.toString.call(it) === "[object Array]";
-}
-
-/**
- * Translates text using the Google Translate API
- * @param text The text to translate
- * @param targetLang The target languate
- */
-export async function translateText(text: string, targetLang: string): Promise<string> {
-	if (targetLang === "en") return text;
-	try {
-		const url = \`http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=\${targetLang}&dt=t&q=\${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8\`;
-		const response = await axios({url, timeout: 5000});
-		if (isArray(response.data)) {
-			// we got a valid response
-			return response.data[0][0][0];
-		}
-		throw new Error("Invalid response for translate request");
-	} catch (e) {
-		throw new Error(\`Could not translate to "\${targetLang}": \${e}\`);
-	}
-	return text;
-}
-`;
-    return template.trim();
+    return createAdapter_1.readFile("tools.raw.ts", __dirname);
 });
diff --git a/build/templates/tsconfig.build.json.js b/build/templates/tsconfig.build.json.js
index 9279c6c..6b22178 100644
--- a/build/templates/tsconfig.build.json.js
+++ b/build/templates/tsconfig.build.json.js
@@ -1,24 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     if (!useTypeScript)
         return;
-    const template = `
-{
-	"extends": "./tsconfig.json",
-	// Modified config to only compile .ts-files in the src dir
-	"compilerOptions": {
-		"allowJs": false,
-		"checkJs": false,
-		"noEmit": false,
-		"declaration": false
-	},
-	"include": [
-		"src/**/*.ts"
-	],
-	"exclude": [
-		"src/**/*.test.ts"
-	]
-}`;
-    return template.trim();
+    return createAdapter_1.readFile("tsconfig.build.raw.json", __dirname);
 });
diff --git a/src/templates/tsconfig.build.json.ts b/build/templates/tsconfig.build.raw.json
similarity index 52%
rename from src/templates/tsconfig.build.json.ts
rename to build/templates/tsconfig.build.raw.json
index ec54489..e818694 100644
--- a/src/templates/tsconfig.build.json.ts
+++ b/build/templates/tsconfig.build.raw.json
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../lib/createAdapter";
-
-export = (answers => {
-
-	const useTypeScript = answers.language === "TypeScript";
-	if (!useTypeScript) return;
-
-	const template = `
 {
 	"extends": "./tsconfig.json",
 	// Modified config to only compile .ts-files in the src dir
@@ -21,6 +13,4 @@ export = (answers => {
 	"exclude": [
 		"src/**/*.test.ts"
 	]
-}`;
-	return template.trim();
-}) as TemplateFunction;
+}
diff --git a/maintenance/generateTemplateIndex.ts b/maintenance/generateTemplateIndex.ts
index d5c27af..cdd92a0 100644
--- a/maintenance/generateTemplateIndex.ts
+++ b/maintenance/generateTemplateIndex.ts
@@ -3,13 +3,15 @@ import * as os from "os";
 import * as path from "path";
 import { enumFilesRecursiveSync } from "../src/lib/tools";
 
-const templateDir = path.join(__dirname, "../src/templates");
+const templateDir = path.join(__dirname, "../templates");
 
 const allTemplateFiles = enumFilesRecursiveSync(
 	templateDir,
 	(name, parentDir) => {
 		// Don't include the index file
 		if (name === "index.ts" && parentDir === templateDir) return false;
+		// Don't include *.raw.* files
+		if (/\.raw\./.test(name)) return false;
 		// But include all directories and .ts-files in /templates
 		const fullName = path.join(parentDir, name);
 		const isDirectory = fs.statSync(fullName).isDirectory();
@@ -36,7 +38,7 @@ const indexContent = `
 // and contains references to all defined templates.
 // Do not edit it by hand or your changes will be lost!
 
-import { TemplateFunction } from "../lib/createAdapter";
+import { TemplateFunction } from "../src/lib/createAdapter";
 
 const templates: { name: string, templateFunction: TemplateFunction }[] = [
 ${templatePaths.join(os.EOL)}
@@ -44,4 +46,4 @@ ${templatePaths.join(os.EOL)}
 export = templates;
 `.trimLeft();
 
-fs.writeFileSync(path.resolve(__dirname, "../src/templates", "index.ts"), indexContent);
+fs.writeFileSync(path.resolve(__dirname, "../templates", "index.ts"), indexContent);
diff --git a/package-lock.json b/package-lock.json
index ddfa9c9..4d7e8e3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -629,6 +629,12 @@
       "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
       "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
     },
+    "camelcase": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+      "dev": true
+    },
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -769,6 +775,135 @@
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
+    "copyfiles": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.1.0.tgz",
+      "integrity": "sha512-cAeDE0vL/koE9WSEGxqPpSyvU638Kgfu6wfrnj7kqp9FWa1CWsU54Coo6sdYZP4GstWa39tL/wIVJWfXcujgNA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.0.5",
+        "minimatch": "^3.0.3",
+        "mkdirp": "^0.5.1",
+        "noms": "0.0.0",
+        "through2": "^2.0.1",
+        "yargs": "^11.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "dev": true,
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "invert-kv": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+          "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+          "dev": true
+        },
+        "lcid": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+          "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+          "dev": true,
+          "requires": {
+            "invert-kv": "^1.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+          "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+          "dev": true,
+          "requires": {
+            "p-locate": "^2.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "mem": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+          "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "os-locale": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+          "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+          "dev": true,
+          "requires": {
+            "execa": "^0.7.0",
+            "lcid": "^1.0.0",
+            "mem": "^1.1.0"
+          }
+        },
+        "p-limit": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+          "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+          "dev": true,
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+          "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+          "dev": true,
+          "requires": {
+            "p-limit": "^1.1.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+          "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+          "dev": true
+        },
+        "y18n": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+          "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "11.1.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
+          "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
+          "dev": true,
+          "requires": {
+            "cliui": "^4.0.0",
+            "decamelize": "^1.1.1",
+            "find-up": "^2.1.0",
+            "get-caller-file": "^1.0.1",
+            "os-locale": "^2.0.0",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^1.0.1",
+            "set-blocking": "^2.0.0",
+            "string-width": "^2.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^3.2.1",
+            "yargs-parser": "^9.0.2"
+          }
+        },
+        "yargs-parser": {
+          "version": "9.0.2",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz",
+          "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=",
+          "dev": true,
+          "requires": {
+            "camelcase": "^4.1.0"
+          }
+        }
+      }
+    },
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -1025,6 +1160,34 @@
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
       "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
     },
+    "execa": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+      "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^5.0.1",
+        "get-stream": "^3.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        }
+      }
+    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -1422,6 +1585,12 @@
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+      "dev": true
+    },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1598,6 +1767,16 @@
       "integrity": "sha512-hcnW80h3j2lbUfFdMArd5UPA/vxZJ+G8vobd+wg3nVEQA0EigStbYcrG030FJxL6xiDDPEkoMatV9xIh5OecQQ==",
       "dev": true
     },
+    "lru-cache": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+      "dev": true,
+      "requires": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
     "make-error": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
@@ -1784,6 +1963,16 @@
         }
       }
     },
+    "noms": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
+      "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "~1.0.31"
+      }
+    },
     "normalize-glob": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/normalize-glob/-/normalize-glob-0.0.1.tgz",
@@ -3074,6 +3263,12 @@
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
       "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ=="
     },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
     "path-is-absolute": {
       "version": "1.0.0",
       "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz",
@@ -3143,6 +3338,12 @@
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
       "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
     },
+    "process-nextick-args": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+      "dev": true
+    },
     "progress": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -3159,6 +3360,12 @@
         "resolve": "~1.8.1"
       }
     },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
+    },
     "psl": {
       "version": "1.1.29",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
@@ -3176,6 +3383,18 @@
       "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
       "dev": true
     },
+    "readable-stream": {
+      "version": "1.0.34",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+      "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
     "reflect-metadata": {
       "version": "0.1.12",
       "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz",
@@ -3473,6 +3692,12 @@
         }
       }
     },
+    "string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+      "dev": true
+    },
     "strip-ansi": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -3524,6 +3749,48 @@
       "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
       "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
     },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "2.3.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "dev": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
     "tmp": {
       "version": "0.0.33",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -3691,6 +3958,12 @@
         "punycode": "^2.1.0"
       }
     },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
     "uuid": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
@@ -3768,11 +4041,23 @@
         "mkdirp": "^0.5.1"
       }
     },
+    "xtend": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+      "dev": true
+    },
     "y18n": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
       "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
     },
+    "yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "dev": true
+    },
     "yargs": {
       "version": "12.0.5",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
diff --git a/package.json b/package.json
index c1621c3..2619ca7 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
     "chai": "^4.2.0",
     "chai-as-promised": "^7.1.1",
     "colors": "^1.3.2",
+    "copyfiles": "^2.1.0",
     "coveralls": "^3.0.2",
     "mocha": "^5.2.0",
     "nyc": "^13.1.0",
@@ -69,6 +70,7 @@
     "prebuild:cleanBuildDir": "rimraf ./build",
     "prebuild": "npm run prebuild:cleanBuildDir && npm run prebuild:linkTemplates && npm run prebuild:cacheLicenses",
     "build": "tsc -p tsconfig.build.json",
+    "postbuild": "copyfiles \"templates/**/*.raw.*\" build",
     "watch": "tsc -p tsconfig.build.json --watch",
     "test:ts": "mocha",
     "test": "mocha",
diff --git a/src/lib/createAdapter.ts b/src/lib/createAdapter.ts
index 2173973..cbc900f 100644
--- a/src/lib/createAdapter.ts
+++ b/src/lib/createAdapter.ts
@@ -2,9 +2,9 @@ import { isArray } from "alcalzone-shared/typeguards";
 import * as fs from "fs-extra";
 import * as os from "os";
 import * as path from "path";
-import * as templateFiles from "../templates";
+import * as templateFiles from "../../templates";
 import { Answers, AnswerValue, Condition } from "./questions";
-import { indentWithSpaces, indentWithTabs, jsFixQuotes, tsFixQuotes } from "./tools";
+import { getOwnVersion, indentWithSpaces, indentWithTabs, jsFixQuotes, tsFixQuotes } from "./tools";
 
 interface AnswersMeta {
 	creatorVersion: string;
@@ -44,17 +44,17 @@ export function testCondition(condition: Condition | Condition[] | undefined, an
 }
 
 export async function createFiles(answers: Answers): Promise<File[]> {
-	const creatorVersion: string = require("../../package.json").version;
+	const creatorVersion: string = getOwnVersion();
 	const answersWithMeta: Answers & AnswersMeta = {
 		...answers,
 		creatorVersion,
 	};
 	const files = await Promise.all(
-		templateFiles.map(async ({name, templateFunction}) => {
+		templateFiles.map(async ({ name, templateFunction }) => {
 			const customPath = typeof templateFunction.customPath === "function" ? templateFunction.customPath(answersWithMeta)
 				: typeof templateFunction.customPath === "string" ? templateFunction.customPath
-				: name.replace(/\.ts$/i, "")
-			;
+					: name.replace(/\.ts$/i, "")
+				;
 			const templateResult = templateFunction(answersWithMeta);
 			return {
 				name: customPath,
@@ -77,7 +77,7 @@ function formatFiles(answers: Answers, files: File[]): File[] {
 			.replace(/\r\n/g, "\n")
 			.replace(/^(\s*\n){2,}/gm, "\n")
 			.replace(/\n/g, os.EOL)
-		;
+			;
 	};
 	const trimWhitespaceLines = (text: string) => text && text.replace(/^[ \t]+$/gm, "");
 	const formatter = (text: string) => trimWhitespaceLines(removeEmptyLines(indentation(text)));
@@ -100,3 +100,9 @@ export async function writeFiles(targetDir: string, files: File[]) {
 		await fs.outputFile(path.join(targetDir, file.name), file.content, typeof file.content === "string" ? "utf8" : undefined);
 	}
 }
+
+export async function readFile(file: string, relativeTo: string, binary: boolean = false): Promise<string | Buffer> {
+	const absolutePath = path.join(relativeTo, file);
+	if (binary) return fs.readFile(absolutePath);
+	else return fs.readFile(absolutePath, "utf8");
+}
diff --git a/src/lib/questions.ts b/src/lib/questions.ts
index becd86d..b18fa22 100644
--- a/src/lib/questions.ts
+++ b/src/lib/questions.ts
@@ -4,6 +4,7 @@ import { prompt } from "enquirer";
 import { checkAdapterName, checkAuthorName, checkEmail, checkMinSelections, CheckResult, checkTitle, transformAdapterName, transformDescription } from "./actionsAndTransformers";
 import { testCondition } from "./createAdapter";
 import { licenses } from "./licenses";
+import { getOwnVersion } from "./tools";
 
 // Sadly, Enquirer does not export the PromptOptions type
 // tslint:disable-next-line:ban-types
@@ -49,14 +50,11 @@ function styledMultiselect<T extends Pick<Question, Exclude<keyof Question, "typ
 	});
 }
 
-// tslint:disable-next-line:no-var-requires
-const ownVersion = require("../../package.json").version;
-
 /** All questions and the corresponding text lines */
 export const questionsAndText: (Question | QuestionGroup | string)[] = [
 	"",
 	green.bold("====================================================="),
-	green.bold(`   Welcome to the ioBroker adapter creator v${ownVersion}!`),
+	green.bold(`   Welcome to the ioBroker adapter creator v${getOwnVersion()}!`),
 	green.bold("====================================================="),
 	"",
 	gray(`You can cancel at any point by pressing Ctrl+C.`),
diff --git a/src/lib/tools.ts b/src/lib/tools.ts
index 6d7941a..5a89de1 100644
--- a/src/lib/tools.ts
+++ b/src/lib/tools.ts
@@ -265,3 +265,15 @@ export function tsFixQuotes(sourceText: string, quotes: keyof typeof Quotemark):
 function escapeQuotes(str: string, newQuotes: Quotemark, oldQuotes: Quotemark) {
 	return str.replace(new RegExp(newQuotes, "g"), `\\${newQuotes}`).replace(new RegExp(`\\\\${oldQuotes}`, "g"), oldQuotes);
 }
+
+export function getOwnVersion(): string {
+	for (const jsonPath of [
+		"../../package.json",
+		"../../../package.json",
+	]) {
+		try {
+			return require(jsonPath).version;
+		} catch (e) { /* OK */ }
+	}
+	return "unknown";
+}
diff --git a/src/templates/admin/icon.png.ts b/src/templates/admin/icon.png.ts
deleted file mode 100644
index b7f88fc..0000000
--- a/src/templates/admin/icon.png.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import * as fs from "fs-extra";
-import * as path from "path";
-import { TemplateFunction } from "../../lib/createAdapter";
-
-const templateFunction: TemplateFunction = () => fs.readFile(path.join(__dirname, "../../../adapter-creator.png"));
-
-templateFunction.customPath = answers => `admin/${answers.adapterName}.png`;
-templateFunction.noReformat = true; // Don't format binary files
-export = templateFunction;
diff --git a/src/templates/admin/style.css.ts b/src/templates/admin/style.css.ts
deleted file mode 100644
index 65c7625..0000000
--- a/src/templates/admin/style.css.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { TemplateFunction } from "../../lib/createAdapter";
-
-export = (answers => {
-
-	const isAdapter = answers.features.indexOf("adapter") > -1;
-	if (!isAdapter) return;
-
-	const template = `
-/* You can delete those if you want. I just found them very helpful */
-* {
-	box-sizing: border-box
-}
-.m {
-	/* Don't cut off dropdowns! */
-	overflow: initial;
-}
-
-/* Add your styles here */
-
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/src/templates/LICENSE.ts b/templates/LICENSE.ts
similarity index 60%
rename from src/templates/LICENSE.ts
rename to templates/LICENSE.ts
index ba1eaa8..025fd57 100644
--- a/src/templates/LICENSE.ts
+++ b/templates/LICENSE.ts
@@ -1,5 +1,5 @@
-import { TemplateFunction } from "../lib/createAdapter";
-import { formatLicense } from "../lib/tools";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+import { formatLicense } from "../src/lib/tools";
 
 export = (answers => {
 	return answers.license
diff --git a/src/templates/README.md.ts b/templates/README.md.ts
similarity index 94%
rename from src/templates/README.md.ts
rename to templates/README.md.ts
index 075ceb2..dc5960c 100644
--- a/src/templates/README.md.ts
+++ b/templates/README.md.ts
@@ -1,5 +1,5 @@
-import { TemplateFunction } from "../lib/createAdapter";
-import { formatLicense } from "../lib/tools";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+import { formatLicense } from "../src/lib/tools";
 
 export = (answers => {
 
diff --git a/src/templates/_eslintrc.json.ts b/templates/_eslintrc.json.ts
similarity index 94%
rename from src/templates/_eslintrc.json.ts
rename to templates/_eslintrc.json.ts
index f6f9e5e..25cd3f2 100644
--- a/src/templates/_eslintrc.json.ts
+++ b/templates/_eslintrc.json.ts
@@ -1,5 +1,5 @@
 import * as JSON5 from "json5";
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/src/templates/_gitignore.ts b/templates/_gitignore.ts
similarity index 89%
rename from src/templates/_gitignore.ts
rename to templates/_gitignore.ts
index 96eecc3..a8296de 100644
--- a/src/templates/_gitignore.ts
+++ b/templates/_gitignore.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/src/templates/_npmignore.ts b/templates/_npmignore.ts
similarity index 93%
rename from src/templates/_npmignore.ts
rename to templates/_npmignore.ts
index d126b5b..49dd5a7 100644
--- a/src/templates/_npmignore.ts
+++ b/templates/_npmignore.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/templates/_travis.raw.yml b/templates/_travis.raw.yml
new file mode 100644
index 0000000..3f99e77
--- /dev/null
+++ b/templates/_travis.raw.yml
@@ -0,0 +1,32 @@
+os:
+  - linux
+  - osx
+  - windows
+
+language: node_js
+node_js:
+  - '6'
+  - '8'
+  - '10'
+
+env:
+  - CXX=g++-4.8
+addons:
+  apt:
+    sources:
+      - ubuntu-toolchain-r-test
+    packages:
+      - g++-4.9
+
+before_install:
+  - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then CC=gcc-4.9; fi'
+  - 'if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then CC=g++-4.9; fi'
+before_script:
+  - export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
+  - 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm; fi'
+  - npm -v
+  - npm install winston@2.3.1
+  - 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
+script:
+  - npm run test:package
+  - npm run test:iobroker
diff --git a/templates/_travis.yml.ts b/templates/_travis.yml.ts
new file mode 100644
index 0000000..8aaf649
--- /dev/null
+++ b/templates/_travis.yml.ts
@@ -0,0 +1,14 @@
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+
+const templateFunction: TemplateFunction = answers => {
+
+	const isAdapter = answers.features.indexOf("adapter") > -1;
+	if (!isAdapter) return;
+
+	return readFile("_travis.raw.yml", __dirname);
+};
+
+templateFunction.customPath = ".travis.yml";
+// .travis.yml is always formatted with 2 spaces
+templateFunction.noReformat = true;
+export = templateFunction;
diff --git a/src/templates/admin/admin.d.ts.ts b/templates/admin/admin.d.ts.ts
similarity index 57%
rename from src/templates/admin/admin.d.ts.ts
rename to templates/admin/admin.d.ts.ts
index 5e09407..a855906 100644
--- a/src/templates/admin/admin.d.ts.ts
+++ b/templates/admin/admin.d.ts.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
@@ -6,8 +6,5 @@ export = (answers => {
 	const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
 	if (!useTypeScript && !useTypeChecking) return;
 
-	const template = `
-declare let systemDictionary: Record<string, Record<string, string>>;
-`;
-	return template.trim();
+	return readFile("admin.raw.d.ts", __dirname);
 }) as TemplateFunction;
diff --git a/templates/admin/admin.raw.d.ts b/templates/admin/admin.raw.d.ts
new file mode 100644
index 0000000..13444d9
--- /dev/null
+++ b/templates/admin/admin.raw.d.ts
@@ -0,0 +1,1 @@
+declare let systemDictionary: Record<string, Record<string, string>>;
diff --git a/templates/admin/icon.png.ts b/templates/admin/icon.png.ts
new file mode 100644
index 0000000..8c257f2
--- /dev/null
+++ b/templates/admin/icon.png.ts
@@ -0,0 +1,6 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+const templateFunction: TemplateFunction = () => readFile("../../adapter-creator.png", __dirname, true);
+templateFunction.customPath = answers => `admin/${answers.adapterName}.png`;
+templateFunction.noReformat = true; // Don't format binary files
+export = templateFunction;
diff --git a/src/templates/admin/index_m.html.ts b/templates/admin/index_m.html.ts
similarity index 97%
rename from src/templates/admin/index_m.html.ts
rename to templates/admin/index_m.html.ts
index 2916e3b..9ed3d93 100644
--- a/src/templates/admin/index_m.html.ts
+++ b/templates/admin/index_m.html.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/templates/admin/style.css.ts b/templates/admin/style.css.ts
new file mode 100644
index 0000000..6c7b96d
--- /dev/null
+++ b/templates/admin/style.css.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+export = (answers => {
+
+	const isAdapter = answers.features.indexOf("adapter") > -1;
+	if (!isAdapter) return;
+
+	return readFile("style.raw.css", __dirname);
+}) as TemplateFunction;
diff --git a/templates/admin/style.raw.css b/templates/admin/style.raw.css
new file mode 100644
index 0000000..5d68855
--- /dev/null
+++ b/templates/admin/style.raw.css
@@ -0,0 +1,10 @@
+/* You can delete those if you want. I just found them very helpful */
+* {
+	box-sizing: border-box
+}
+.m {
+	/* Don't cut off dropdowns! */
+	overflow: initial;
+}
+
+/* Add your styles here */
diff --git a/src/templates/admin/tsconfig.json.ts b/templates/admin/tsconfig.json.ts
similarity index 55%
rename from src/templates/admin/tsconfig.json.ts
rename to templates/admin/tsconfig.json.ts
index 11e4bc1..0eaf062 100644
--- a/src/templates/admin/tsconfig.json.ts
+++ b/templates/admin/tsconfig.json.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
@@ -6,14 +6,5 @@ export = (answers => {
 	const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
 	if (!useTypeScript && !useTypeChecking) return;
 
-	const template = `
-{
-	"extends": "../tsconfig.json",
-	"include": [
-		"./**/*.d.ts",
-		"./**/*.js"
-	]
-}
-`;
-	return template.trim();
+	return readFile("tsconfig.raw.json", __dirname);
 }) as TemplateFunction;
diff --git a/templates/admin/tsconfig.raw.json b/templates/admin/tsconfig.raw.json
new file mode 100644
index 0000000..6dcddfa
--- /dev/null
+++ b/templates/admin/tsconfig.raw.json
@@ -0,0 +1,7 @@
+{
+	"extends": "../tsconfig.json",
+	"include": [
+		"./**/*.d.ts",
+		"./**/*.js"
+	]
+}
diff --git a/src/templates/admin/words.js.ts b/templates/admin/words.js.ts
similarity index 95%
rename from src/templates/admin/words.js.ts
rename to templates/admin/words.js.ts
index c0c5b7a..a2d1409 100644
--- a/src/templates/admin/words.js.ts
+++ b/templates/admin/words.js.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/src/templates/gulpfile.js.ts b/templates/gulpfile.js.ts
similarity index 99%
rename from src/templates/gulpfile.js.ts
rename to templates/gulpfile.js.ts
index 7f90a95..497faa0 100644
--- a/src/templates/gulpfile.js.ts
+++ b/templates/gulpfile.js.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/src/templates/index.ts b/templates/index.ts
similarity index 98%
rename from src/templates/index.ts
rename to templates/index.ts
index 95c5552..3e99174 100644
--- a/src/templates/index.ts
+++ b/templates/index.ts
@@ -2,7 +2,7 @@
 // and contains references to all defined templates.
 // Do not edit it by hand or your changes will be lost!
 
-import { TemplateFunction } from "../lib/createAdapter";
+import { TemplateFunction } from "../src/lib/createAdapter";
 
 const templates: { name: string, templateFunction: TemplateFunction }[] = [
 	{ name: "admin/admin.d.ts.ts", templateFunction: require("./admin/admin.d.ts") },
diff --git a/src/templates/io-package.json.ts b/templates/io-package.json.ts
similarity index 95%
rename from src/templates/io-package.json.ts
rename to templates/io-package.json.ts
index fc26518..56e6d5f 100644
--- a/src/templates/io-package.json.ts
+++ b/templates/io-package.json.ts
@@ -1,7 +1,7 @@
 import { composeObject } from "alcalzone-shared/objects";
 import * as JSON5 from "json5";
-import { TemplateFunction } from "../lib/createAdapter";
-import { translateText } from "../lib/tools";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+import { translateText } from "../src/lib/tools";
 
 export = (async answers => {
 
diff --git a/src/templates/lib/adapter-config.d.ts.ts b/templates/lib/adapter-config.d.ts.ts
similarity index 94%
rename from src/templates/lib/adapter-config.d.ts.ts
rename to templates/lib/adapter-config.d.ts.ts
index 370d819..f5cd56a 100644
--- a/src/templates/lib/adapter-config.d.ts.ts
+++ b/templates/lib/adapter-config.d.ts.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/templates/lib/tools.js.ts b/templates/lib/tools.js.ts
new file mode 100644
index 0000000..6b01f73
--- /dev/null
+++ b/templates/lib/tools.js.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+export = (answers => {
+
+	const useJavaScript = answers.language === "JavaScript";
+	if (!useJavaScript) return;
+
+	return readFile("tools.raw.js", __dirname);
+}) as TemplateFunction;
diff --git a/templates/lib/tools.raw.js b/templates/lib/tools.raw.js
new file mode 100644
index 0000000..4f538ee
--- /dev/null
+++ b/templates/lib/tools.raw.js
@@ -0,0 +1,52 @@
+const axios = require("axios");
+
+/**
+ * Tests whether the given variable is a real object and not an Array
+ * @param {any} it The variable to test
+ * @returns {it is Record<string, any>}
+ */
+function isObject(it) {
+	// This is necessary because:
+	// typeof null === 'object'
+	// typeof [] === 'object'
+	// [] instanceof Object === true
+	return Object.prototype.toString.call(it) === "[object Object]";
+}
+
+/**
+ * Tests whether the given variable is really an Array
+ * @param {any} it The variable to test
+ * @returns {it is any[]}
+ */
+function isArray(it) {
+	if (Array.isArray != null) return Array.isArray(it);
+	return Object.prototype.toString.call(it) === "[object Array]";
+}
+
+/**
+ * Translates text using the Google Translate API
+ * @param {string} text The text to translate
+ * @param {string}targetLang The target languate
+ * @returns {Promise<string>}
+ */
+async function translateText(text, targetLang) {
+	if (targetLang === "en") return text;
+	try {
+		const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
+		const response = await axios({url, timeout: 5000});
+		if (isArray(response.data)) {
+			// we got a valid response
+			return response.data[0][0][0];
+		}
+		throw new Error("Invalid response for translate request");
+	} catch (e) {
+		throw new Error(`Could not translate to "${targetLang}": ${e}`);
+	}
+	return text;
+}
+
+module.exports = {
+	isArray,
+	isObject,
+	translateText
+};
diff --git a/src/templates/main.js.ts b/templates/main.js.ts
similarity index 98%
rename from src/templates/main.js.ts
rename to templates/main.js.ts
index d0eac77..0d8db1e 100644
--- a/src/templates/main.js.ts
+++ b/templates/main.js.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 export = (async answers => {
 
diff --git a/src/templates/package.json.ts b/templates/package.json.ts
similarity index 96%
rename from src/templates/package.json.ts
rename to templates/package.json.ts
index 50b0d89..9817c56 100644
--- a/src/templates/package.json.ts
+++ b/templates/package.json.ts
@@ -1,7 +1,7 @@
 import { promiseSequence } from "alcalzone-shared/async";
 import * as JSON5 from "json5";
-import { TemplateFunction } from "../lib/createAdapter";
-import { fetchPackageVersion } from "../lib/fetchVersions";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+import { fetchPackageVersion } from "../src/lib/fetchVersions";
 
 const templateFunction: TemplateFunction = async answers => {
 
diff --git a/templates/src/lib/tools.raw.ts b/templates/src/lib/tools.raw.ts
new file mode 100644
index 0000000..796a52a
--- /dev/null
+++ b/templates/src/lib/tools.raw.ts
@@ -0,0 +1,43 @@
+import axios from "axios";
+
+/**
+ * Tests whether the given variable is a real object and not an Array
+ * @param it The variable to test
+ */
+export function isObject(it: any): it is object {
+	// This is necessary because:
+	// typeof null === 'object'
+	// typeof [] === 'object'
+	// [] instanceof Object === true
+	return Object.prototype.toString.call(it) === "[object Object]";
+}
+
+/**
+ * Tests whether the given variable is really an Array
+ * @param it The variable to test
+ */
+export function isArray(it: any): it is any[] {
+	if (Array.isArray != null) return Array.isArray(it);
+	return Object.prototype.toString.call(it) === "[object Array]";
+}
+
+/**
+ * Translates text using the Google Translate API
+ * @param text The text to translate
+ * @param targetLang The target languate
+ */
+export async function translateText(text: string, targetLang: string): Promise<string> {
+	if (targetLang === "en") return text;
+	try {
+		const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
+		const response = await axios({url, timeout: 5000});
+		if (isArray(response.data)) {
+			// we got a valid response
+			return response.data[0][0][0];
+		}
+		throw new Error("Invalid response for translate request");
+	} catch (e) {
+		throw new Error(`Could not translate to "${targetLang}": ${e}`);
+	}
+	return text;
+}
diff --git a/templates/src/lib/tools.ts.ts b/templates/src/lib/tools.ts.ts
new file mode 100644
index 0000000..75e93f2
--- /dev/null
+++ b/templates/src/lib/tools.ts.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../../../src/lib/createAdapter";
+
+export = (answers => {
+
+	const useTypeScript = answers.language === "TypeScript";
+	if (!useTypeScript) return;
+
+	return readFile("tools.raw.ts", __dirname);
+}) as TemplateFunction;
diff --git a/src/templates/src/main.ts.ts b/templates/src/main.ts.ts
similarity index 98%
rename from src/templates/src/main.ts.ts
rename to templates/src/main.ts.ts
index d4e878f..b9cf235 100644
--- a/src/templates/src/main.ts.ts
+++ b/templates/src/main.ts.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/templates/tsconfig.build.json.ts b/templates/tsconfig.build.json.ts
new file mode 100644
index 0000000..2e8150d
--- /dev/null
+++ b/templates/tsconfig.build.json.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+
+export = (answers => {
+
+	const useTypeScript = answers.language === "TypeScript";
+	if (!useTypeScript) return;
+
+	return readFile("tsconfig.build.raw.json", __dirname);
+}) as TemplateFunction;
diff --git a/templates/tsconfig.build.raw.json b/templates/tsconfig.build.raw.json
new file mode 100644
index 0000000..e818694
--- /dev/null
+++ b/templates/tsconfig.build.raw.json
@@ -0,0 +1,16 @@
+{
+	"extends": "./tsconfig.json",
+	// Modified config to only compile .ts-files in the src dir
+	"compilerOptions": {
+		"allowJs": false,
+		"checkJs": false,
+		"noEmit": false,
+		"declaration": false
+	},
+	"include": [
+		"src/**/*.ts"
+	],
+	"exclude": [
+		"src/**/*.test.ts"
+	]
+}
diff --git a/src/templates/tsconfig.json.ts b/templates/tsconfig.json.ts
similarity index 95%
rename from src/templates/tsconfig.json.ts
rename to templates/tsconfig.json.ts
index 9986897..5243119 100644
--- a/src/templates/tsconfig.json.ts
+++ b/templates/tsconfig.json.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/src/templates/tslint.json.ts b/templates/tslint.json.ts
similarity index 94%
rename from src/templates/tslint.json.ts
rename to templates/tslint.json.ts
index 5824c0c..43f467d 100644
--- a/src/templates/tslint.json.ts
+++ b/templates/tslint.json.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../lib/createAdapter";
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/src/templates/widgets/style.css.ts b/templates/widgets/style.css.ts
similarity index 85%
rename from src/templates/widgets/style.css.ts
rename to templates/widgets/style.css.ts
index 92f3ba4..e996bb3 100644
--- a/src/templates/widgets/style.css.ts
+++ b/templates/widgets/style.css.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/src/templates/widgets/template.html.ts b/templates/widgets/template.html.ts
similarity index 99%
rename from src/templates/widgets/template.html.ts
rename to templates/widgets/template.html.ts
index 4909695..48f1dd4 100644
--- a/src/templates/widgets/template.html.ts
+++ b/templates/widgets/template.html.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/src/templates/widgets/template.js.ts b/templates/widgets/template.js.ts
similarity index 98%
rename from src/templates/widgets/template.js.ts
rename to templates/widgets/template.js.ts
index e77b0ac..3b06d04 100644
--- a/src/templates/widgets/template.js.ts
+++ b/templates/widgets/template.js.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 const templateFunction: TemplateFunction = answers => {
 
diff --git a/tsconfig.build.json b/tsconfig.build.json
index 6957e4e..85b1281 100644
--- a/tsconfig.build.json
+++ b/tsconfig.build.json
@@ -6,9 +6,11 @@
 		"declaration": false,
 	},
 	"include": [
-		"src/**/*.ts"
+		"src/**/*.ts",
+		"templates/**/*.ts"
 	],
 	"exclude": [
-		"src/**/*.test.ts"
+		"src/**/*.test.ts",
+		"templates/**/*.raw.*"
 	]
 }


Test patch:
diff --git a/build/templates/main.test.js.js b/build/templates/main.test.js.js
index da158cf..53b6a0d 100644
--- a/build/templates/main.test.js.js
+++ b/build/templates/main.test.js.js
@@ -1,39 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../src/lib/createAdapter");
 module.exports = (answers => {
     const useJavaScript = answers.language === "JavaScript";
     if (!useJavaScript)
         return;
-    const template = `
-"use strict";
-
-/**
- * This is a dummy TypeScript test file using chai and mocha
- *
- * It's automatically excluded from npm and its build output is excluded from both git and npm.
- * It is advised to test all your modules with accompanying *.test.js-files
- */
-
-// tslint:disable:no-unused-expression
-
-const { expect } = require("chai");
-// import { functionToTest } from "./moduleToTest";
-
-describe("module to test => function to test", () => {
-	// initializing logic
-	const expected = 5;
-
-	it(\`should return \${expected}\`, () => {
-		const result = 5;
-		// assign result a value from functionToTest
-		expect(result).to.equal(expected);
-		// or using the should() syntax
-		result.should.equal(expected);
-	});
-	// ... more tests => it
-
-});
-
-// ... more test suites => describe
-`;
-    return template.trim();
+    return createAdapter_1.readFile("main.test.raw.js", __dirname);
 });
diff --git a/src/templates/main.test.js.ts b/build/templates/main.test.raw.js
similarity index 71%
rename from src/templates/main.test.js.ts
rename to build/templates/main.test.raw.js
index 50cb724..307b4c6 100644
--- a/src/templates/main.test.js.ts
+++ b/build/templates/main.test.raw.js
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../lib/createAdapter";
-
-export = (answers => {
-
-	const useJavaScript = answers.language === "JavaScript";
-	if (!useJavaScript) return;
-
-	const template = `
 "use strict";
 
 /**
@@ -24,7 +16,7 @@ describe("module to test => function to test", () => {
 	// initializing logic
 	const expected = 5;
 
-	it(\`should return \${expected}\`, () => {
+	it(`should return ${expected}`, () => {
 		const result = 5;
 		// assign result a value from functionToTest
 		expect(result).to.equal(expected);
@@ -36,6 +28,3 @@ describe("module to test => function to test", () => {
 });
 
 // ... more test suites => describe
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/src/templates/src/main.test.ts.ts b/build/templates/src/main.test.raw.ts
similarity index 71%
rename from src/templates/src/main.test.ts.ts
rename to build/templates/src/main.test.raw.ts
index c88f6a4..513a325 100644
--- a/src/templates/src/main.test.ts.ts
+++ b/build/templates/src/main.test.raw.ts
@@ -1,11 +1,3 @@
-import { TemplateFunction } from "../../lib/createAdapter";
-
-export = (answers => {
-
-	const useTypeScript = answers.language === "TypeScript";
-	if (!useTypeScript) return;
-
-	const template = `
 /**
  * This is a dummy TypeScript test file using chai and mocha
  *
@@ -22,7 +14,7 @@ describe("module to test => function to test", () => {
 	// initializing logic
 	const expected = 5;
 
-	it(\`should return \${expected}\`, () => {
+	it(`should return ${expected}`, () => {
 		const result = 5;
 		// assign result a value from functionToTest
 		expect(result).to.equal(expected);
@@ -34,6 +26,3 @@ describe("module to test => function to test", () => {
 });
 
 // ... more test suites => describe
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/src/main.test.ts.js b/build/templates/src/main.test.ts.js
index 17f04cc..cb230e1 100644
--- a/build/templates/src/main.test.ts.js
+++ b/build/templates/src/main.test.ts.js
@@ -1,37 +1,8 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     if (!useTypeScript)
         return;
-    const template = `
-/**
- * This is a dummy TypeScript test file using chai and mocha
- *
- * It's automatically excluded from npm and its build output is excluded from both git and npm.
- * It is advised to test all your modules with accompanying *.test.ts-files
- */
-
-// tslint:disable:no-unused-expression
-
-import { expect } from "chai";
-// import { functionToTest } from "./moduleToTest";
-
-describe("module to test => function to test", () => {
-	// initializing logic
-	const expected = 5;
-
-	it(\`should return \${expected}\`, () => {
-		const result = 5;
-		// assign result a value from functionToTest
-		expect(result).to.equal(expected);
-		// or using the should() syntax
-		result.should.equal(expected);
-	});
-	// ... more tests => it
-
-});
-
-// ... more test suites => describe
-`;
-    return template.trim();
+    return createAdapter_1.readFile("main.test.raw.ts", __dirname);
 });
diff --git a/build/templates/test/lib/setup.js.js b/build/templates/test/lib/setup.js.js
index 299269c..94cdffc 100644
--- a/build/templates/test/lib/setup.js.js
+++ b/build/templates/test/lib/setup.js.js
@@ -1,732 +1,5 @@
 "use strict";
+const createAdapter_1 = require("../../../src/lib/createAdapter");
 module.exports = (answers => {
-    const template = `
-"use strict";
-
-const fs            = require("fs");
-const path          = require("path");
-const child_process = require("child_process");
-const rootDir       = path.normalize(__dirname + "/../../");
-const adapterName   = path.basename(rootDir);
-const pkg           = require(rootDir + "package.json");
-const debug         = typeof v8debug === "object";
-pkg.main = pkg.main || "main.js";
-
-let adapterStarted = false;
-
-function getAppName() {
-	const parts = __dirname.replace(/\\\\/g, "/").split("/");
-	return parts[parts.length - 3].split(".")[0];
-}
-
-const appName = getAppName().toLowerCase();
-
-let objects;
-let states;
-
-/** @type {undefined | ReturnType<typeof import("child_process")["exec"]>} */
-let pid;
-
-function copyFileSync(source, target) {
-
-	let targetFile = target;
-
-	//if target is a directory a new file with the same name will be created
-	if (fs.existsSync(target)) {
-		if ( fs.lstatSync( target ).isDirectory() ) {
-			targetFile = path.join(target, path.basename(source));
-		}
-	}
-
-	try {
-		fs.writeFileSync(targetFile, fs.readFileSync(source));
-	}
-	catch (err) {
-		console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
-	}
-}
-
-function copyFolderRecursiveSync(source, target, ignore) {
-	let files = [];
-
-	let base = path.basename(source);
-	if (base === adapterName) {
-		base = pkg.name;
-	}
-	//check if folder needs to be created or integrated
-	const targetFolder = path.join(target, base);
-	if (!fs.existsSync(targetFolder)) {
-		fs.mkdirSync(targetFolder);
-	}
-
-	//copy
-	if (fs.lstatSync(source).isDirectory()) {
-		files = fs.readdirSync(source);
-		files.forEach(function (file) {
-			if (ignore && ignore.indexOf(file) !== -1) {
-				return;
-			}
-
-			const curSource = path.join(source, file);
-			const curTarget = path.join(targetFolder, file);
-			if (fs.lstatSync(curSource).isDirectory()) {
-				// ignore grunt files
-				if (file.indexOf("grunt") !== -1) return;
-				if (file === "chai") return;
-				if (file === "mocha") return;
-				copyFolderRecursiveSync(curSource, targetFolder, ignore);
-			} else {
-				copyFileSync(curSource, curTarget);
-			}
-		});
-	}
-}
-
-if (!fs.existsSync(rootDir + "tmp")) {
-	fs.mkdirSync(rootDir + "tmp");
-}
-
-function storeOriginalFiles() {
-	console.log("Store original files...");
-	const dataDir = rootDir + "tmp/" + appName + "-data/";
-
-	let f = fs.readFileSync(dataDir + "objects.json");
-	const objects = JSON.parse(f.toString());
-	if (objects["system.adapter.admin.0"] && objects["system.adapter.admin.0"].common) {
-		objects["system.adapter.admin.0"].common.enabled = false;
-	}
-	if (objects["system.adapter.admin.1"] && objects["system.adapter.admin.1"].common) {
-		objects["system.adapter.admin.1"].common.enabled = false;
-	}
-
-	fs.writeFileSync(dataDir + "objects.json.original", JSON.stringify(objects));
-	try {
-		f = fs.readFileSync(dataDir + "states.json");
-		fs.writeFileSync(dataDir + "states.json.original", f);
-	}
-	catch (err) {
-		console.log("no states.json found - ignore");
-	}
-}
-
-function restoreOriginalFiles() {
-	console.log("restoreOriginalFiles...");
-	const dataDir = rootDir + "tmp/" + appName + "-data/";
-
-	let f = fs.readFileSync(dataDir + "objects.json.original");
-	fs.writeFileSync(dataDir + "objects.json", f);
-	try {
-		f = fs.readFileSync(dataDir + "states.json.original");
-		fs.writeFileSync(dataDir + "states.json", f);
-	}
-	catch (err) {
-		console.log("no states.json.original found - ignore");
-	}
-
-}
-
-function checkIsAdapterInstalled(cb, counter, customName) {
-	customName = customName || pkg.name.split(".").pop();
-	counter = counter || 0;
-	const dataDir = rootDir + "tmp/" + appName + "-data/";
-	console.log("checkIsAdapterInstalled...");
-
-	try {
-		const f = fs.readFileSync(dataDir + "objects.json");
-		const objects = JSON.parse(f.toString());
-		if (objects["system.adapter." + customName + ".0"]) {
-			console.log("checkIsAdapterInstalled: ready!");
-			setTimeout(function () {
-				if (cb) cb();
-			}, 100);
-			return;
-		} else {
-			console.warn("checkIsAdapterInstalled: still not ready");
-		}
-	} catch (err) {
-		// Don't care
-	}
-
-	if (counter > 20) {
-		console.error("checkIsAdapterInstalled: Cannot install!");
-		if (cb) cb("Cannot install");
-	} else {
-		console.log("checkIsAdapterInstalled: wait...");
-		setTimeout(function() {
-			checkIsAdapterInstalled(cb, counter + 1);
-		}, 1000);
-	}
-}
-
-function checkIsControllerInstalled(cb, counter) {
-	counter = counter || 0;
-	const dataDir = rootDir + "tmp/" + appName + "-data/";
-
-	console.log("checkIsControllerInstalled...");
-	try {
-		const f = fs.readFileSync(dataDir + "objects.json");
-		const objects = JSON.parse(f.toString());
-		if (objects["system.adapter.admin.0"]) {
-			console.log("checkIsControllerInstalled: installed!");
-			setTimeout(function () {
-				if (cb) cb();
-			}, 100);
-			return;
-		}
-	} catch (err) {
-		// Don't care
-	}
-
-	if (counter > 20) {
-		console.log("checkIsControllerInstalled: Cannot install!");
-		if (cb) cb("Cannot install");
-	} else {
-		console.log("checkIsControllerInstalled: wait...");
-		setTimeout(function() {
-			checkIsControllerInstalled(cb, counter + 1);
-		}, 1000);
-	}
-}
-
-function installAdapter(customName, cb) {
-	if (typeof customName === "function") {
-		cb = customName;
-		customName = null;
-	}
-	customName = customName || pkg.name.split(".").pop();
-	console.log("Install adapter...");
-	const startFile = "node_modules/" + appName + ".js-controller/" + appName + ".js";
-	// make first install
-	if (debug) {
-		child_process.execSync("node " + startFile + " add " + customName + " --enabled false", {
-			cwd:   rootDir + "tmp",
-			stdio: [0, 1, 2]
-		});
-		checkIsAdapterInstalled(function (error) {
-			if (error) console.error(error);
-			console.log("Adapter installed.");
-			if (cb) cb();
-		});
-	} else {
-		// add controller
-		const _pid = child_process.fork(startFile, ["add", customName, "--enabled", "false"], {
-			cwd:   rootDir + "tmp",
-			stdio: [0, 1, 2, "ipc"]
-		});
-
-		waitForEnd(_pid, function () {
-			checkIsAdapterInstalled(function (error) {
-				if (error) console.error(error);
-				console.log("Adapter installed.");
-				if (cb) cb();
-			});
-		});
-	}
-}
-
-function waitForEnd(_pid, cb) {
-	if (!_pid) {
-		cb(-1, -1);
-		return;
-	}
-	_pid.on("exit", function (code, signal) {
-		if (_pid) {
-			_pid = null;
-			cb(code, signal);
-		}
-	});
-	_pid.on("close", function (code, signal) {
-		if (_pid) {
-			_pid = null;
-			cb(code, signal);
-		}
-	});
-}
-
-function installJsController(cb) {
-	console.log("installJsController...");
-	if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller") ||
-		!fs.existsSync(rootDir + "tmp/" + appName + "-data")) {
-		// try to detect appName.js-controller in node_modules/appName.js-controller
-		// travis CI installs js-controller into node_modules
-		if (fs.existsSync(rootDir + "node_modules/" + appName + ".js-controller")) {
-			console.log('installJsController: no js-controller => copy it from "' + rootDir + "node_modules/" + appName + '.js-controller"');
-			// copy all
-			// stop controller
-			console.log("Stop controller if running...");
-			let _pid;
-			if (debug) {
-				// start controller
-				_pid = child_process.exec("node " + appName + ".js stop", {
-					cwd: rootDir + "node_modules/" + appName + ".js-controller",
-					stdio: [0, 1, 2]
-				});
-			} else {
-				_pid = child_process.fork(appName + ".js", ["stop"], {
-					cwd:   rootDir + "node_modules/" + appName + ".js-controller",
-					stdio: [0, 1, 2, "ipc"]
-				});
-			}
-
-			waitForEnd(_pid, function () {
-				// copy all files into
-				if (!fs.existsSync(rootDir + "tmp")) fs.mkdirSync(rootDir + "tmp");
-				if (!fs.existsSync(rootDir + "tmp/node_modules")) fs.mkdirSync(rootDir + "tmp/node_modules");
-
-				if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller")){
-					console.log("Copy js-controller...");
-					copyFolderRecursiveSync(rootDir + "node_modules/" + appName + ".js-controller", rootDir + "tmp/node_modules/");
-				}
-
-				console.log("Setup js-controller...");
-				let __pid;
-				if (debug) {
-					// start controller
-					_pid = child_process.exec("node " + appName + ".js setup first --console", {
-						cwd: rootDir + "tmp/node_modules/" + appName + ".js-controller",
-						stdio: [0, 1, 2]
-					});
-				} else {
-					__pid = child_process.fork(appName + ".js", ["setup", "first", "--console"], {
-						cwd:   rootDir + "tmp/node_modules/" + appName + ".js-controller",
-						stdio: [0, 1, 2, "ipc"]
-					});
-				}
-				waitForEnd(__pid, function () {
-					checkIsControllerInstalled(function () {
-						// change ports for object and state DBs
-						const config = require(rootDir + "tmp/" + appName + "-data/" + appName + ".json");
-						config.objects.port = 19001;
-						config.states.port  = 19000;
-						fs.writeFileSync(rootDir + "tmp/" + appName + "-data/" + appName + ".json", JSON.stringify(config, null, 2));
-						console.log("Setup finished.");
-
-						copyAdapterToController();
-
-						installAdapter(function () {
-							storeOriginalFiles();
-							if (cb) cb(true);
-						});
-					});
-				});
-			});
-		} else {
-			// check if port 9000 is free, else admin adapter will be added to running instance
-			const Socket = require("net").Socket;
-			const client = new Socket();
-			client.connect(9000, "127.0.0.1", function() {
-				console.error("Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.");
-				process.exit(0);
-			});
-
-			setTimeout(function () {
-				client.destroy();
-				if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller")) {
-					console.log("installJsController: no js-controller => install from git");
-
-					child_process.execSync("npm install https://github.com/" + appName + "/" + appName + ".js-controller/tarball/master --prefix ./  --production", {
-						cwd:   rootDir + "tmp/",
-						stdio: [0, 1, 2]
-					});
-				} else {
-					console.log("Setup js-controller...");
-					if (debug) {
-						// start controller
-						child_process.exec("node " + appName + ".js setup first", {
-							cwd: rootDir + "tmp/node_modules/" + appName + ".js-controller",
-							stdio: [0, 1, 2]
-						});
-					} else {
-						child_process.fork(appName + ".js", ["setup", "first"], {
-							cwd:   rootDir + "tmp/node_modules/" + appName + ".js-controller",
-							stdio: [0, 1, 2, "ipc"]
-						});
-					}
-				}
-
-				// let npm install admin and run setup
-				checkIsControllerInstalled(function () {
-					let _pid;
-
-					if (fs.existsSync(rootDir + "node_modules/" + appName + ".js-controller/" + appName + ".js")) {
-						_pid = child_process.fork(appName + ".js", ["stop"], {
-							cwd:   rootDir + "node_modules/" + appName + ".js-controller",
-							stdio: [0, 1, 2, "ipc"]
-						});
-					}
-
-					waitForEnd(_pid, function () {
-						// change ports for object and state DBs
-						const config = require(rootDir + "tmp/" + appName + "-data/" + appName + ".json");
-						config.objects.port = 19001;
-						config.states.port  = 19000;
-						fs.writeFileSync(rootDir + "tmp/" + appName + "-data/" + appName + ".json", JSON.stringify(config, null, 2));
-
-						copyAdapterToController();
-
-						installAdapter(function () {
-							storeOriginalFiles();
-							if (cb) cb(true);
-						});
-					});
-				});
-			}, 1000);
-		}
-	} else {
-		setTimeout(function () {
-			console.log("installJsController: js-controller installed");
-			if (cb) cb(false);
-		}, 0);
-	}
-}
-
-function copyAdapterToController() {
-	console.log("Copy adapter...");
-	// Copy adapter to tmp/node_modules/appName.adapter
-	copyFolderRecursiveSync(rootDir, rootDir + "tmp/node_modules/", [".idea", "test", "tmp", ".git", appName + ".js-controller"]);
-	console.log("Adapter copied.");
-}
-
-function clearControllerLog() {
-	const dirPath = rootDir + "tmp/log";
-	let files;
-	try {
-		if (fs.existsSync(dirPath)) {
-			console.log("Clear controller log...");
-			files = fs.readdirSync(dirPath);
-		} else {
-			console.log("Create controller log directory...");
-			files = [];
-			fs.mkdirSync(dirPath);
-		}
-	} catch(e) {
-		console.error('Cannot read "' + dirPath + '"');
-		return;
-	}
-	if (files.length > 0) {
-		try {
-			for (let i = 0; i < files.length; i++) {
-				const filePath = dirPath + "/" + files[i];
-				fs.unlinkSync(filePath);
-			}
-			console.log("Controller log cleared");
-		} catch (err) {
-			console.error("cannot clear log: " + err);
-		}
-	}
-}
-
-function clearDB() {
-	const dirPath = rootDir + "tmp/iobroker-data/sqlite";
-	let files;
-	try {
-		if (fs.existsSync(dirPath)) {
-			console.log("Clear sqlite DB...");
-			files = fs.readdirSync(dirPath);
-		} else {
-			console.log("Create controller log directory...");
-			files = [];
-			fs.mkdirSync(dirPath);
-		}
-	} catch(e) {
-		console.error('Cannot read "' + dirPath + '"');
-		return;
-	}
-	if (files.length > 0) {
-		try {
-			for (let i = 0; i < files.length; i++) {
-				const filePath = dirPath + "/" + files[i];
-				fs.unlinkSync(filePath);
-			}
-			console.log("Clear sqlite DB");
-		} catch (err) {
-			console.error("cannot clear DB: " + err);
-		}
-	}
-}
-
-function setupController(cb) {
-	installJsController(function (isInited) {
-		clearControllerLog();
-		clearDB();
-
-		if (!isInited) {
-			restoreOriginalFiles();
-			copyAdapterToController();
-		}
-		// read system.config object
-		const dataDir = rootDir + "tmp/" + appName + "-data/";
-
-		let objs;
-		try {
-			objs = JSON.parse(fs.readFileSync(dataDir + "objects.json", "utf8"));
-		}
-		catch (e) {
-			console.log("ERROR reading/parsing system configuration. Ignore");
-			objs = {"system.config": {}};
-		}
-		if (!objs || !objs["system.config"]) {
-			objs = {"system.config": {}};
-		}
-
-		if (cb) cb(objs["system.config"]);
-	});
-}
-
-function startAdapter(objects, states, callback) {
-	if (adapterStarted) {
-		console.log("Adapter already started ...");
-		if (callback) callback(objects, states);
-		return;
-	}
-	adapterStarted = true;
-	console.log("startAdapter...");
-	if (fs.existsSync(rootDir + "tmp/node_modules/" + pkg.name + "/" + pkg.main)) {
-		try {
-			if (debug) {
-				// start controller
-				pid = child_process.exec("node node_modules/" + pkg.name + "/" + pkg.main + " --console silly", {
-					cwd: rootDir + "tmp",
-					stdio: [0, 1, 2]
-				});
-			} else {
-				// start controller
-				pid = child_process.fork("node_modules/" + pkg.name + "/" + pkg.main, ["--console", "silly"], {
-					cwd:   rootDir + "tmp",
-					stdio: [0, 1, 2, "ipc"]
-				});
-			}
-		} catch (error) {
-			console.error(JSON.stringify(error));
-		}
-	} else {
-		console.error("Cannot find: " + rootDir + "tmp/node_modules/" + pkg.name + "/" + pkg.main);
-	}
-	if (callback) callback(objects, states);
-}
-
-function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
-	if (typeof isStartAdapter === "function") {
-		callback = onStateChange;
-		onStateChange = onObjectChange;
-		onObjectChange = isStartAdapter;
-		isStartAdapter = true;
-	}
-
-	if (onStateChange === undefined) {
-		callback  = onObjectChange;
-		onObjectChange = undefined;
-	}
-
-	if (pid) {
-		console.error("Controller is already started!");
-	} else {
-		console.log("startController...");
-		adapterStarted = false;
-		let isObjectConnected;
-		let isStatesConnected;
-
-		const Objects = require(rootDir + "tmp/node_modules/" + appName + ".js-controller/lib/objects/objectsInMemServer");
-		objects = new Objects({
-			connection: {
-				"type" : "file",
-				"host" : "127.0.0.1",
-				"port" : 19001,
-				"user" : "",
-				"pass" : "",
-				"noFileCache": false,
-				"connectTimeout": 2000
-			},
-			logger: {
-				silly: function (msg) {
-					console.log(msg);
-				},
-				debug: function (msg) {
-					console.log(msg);
-				},
-				info: function (msg) {
-					console.log(msg);
-				},
-				warn: function (msg) {
-					console.warn(msg);
-				},
-				error: function (msg) {
-					console.error(msg);
-				}
-			},
-			connected: function () {
-				isObjectConnected = true;
-				if (isStatesConnected) {
-					console.log("startController: started!");
-					if (isStartAdapter) {
-						startAdapter(objects, states, callback);
-					} else {
-						if (callback) {
-							callback(objects, states);
-							callback = null;
-						}
-					}
-				}
-			},
-			change: onObjectChange
-		});
-
-		// Just open in memory DB itself
-		const States = require(rootDir + "tmp/node_modules/" + appName + ".js-controller/lib/states/statesInMemServer");
-		states = new States({
-			connection: {
-				type: "file",
-				host: "127.0.0.1",
-				port: 19000,
-				options: {
-					auth_pass: null,
-					retry_max_delay: 15000
-				}
-			},
-			logger: {
-				silly: function (msg) {
-					console.log(msg);
-				},
-				debug: function (msg) {
-					console.log(msg);
-				},
-				info: function (msg) {
-					console.log(msg);
-				},
-				warn: function (msg) {
-					console.log(msg);
-				},
-				error: function (msg) {
-					console.log(msg);
-				}
-			},
-			connected: function () {
-				isStatesConnected = true;
-				if (isObjectConnected) {
-					console.log("startController: started!!");
-					if (isStartAdapter) {
-						startAdapter(objects, states, callback);
-					} else {
-						if (callback) {
-							callback(objects, states);
-							callback = null;
-						}
-					}
-				}
-			},
-			change: onStateChange
-		});
-	}
-}
-
-function stopAdapter(cb) {
-	if (!pid) {
-		console.error("Controller is not running!");
-		if (cb) {
-			setTimeout(function () {
-				cb(false);
-			}, 0);
-		}
-	} else {
-		adapterStarted = false;
-		pid.on("exit", function (code, signal) {
-			if (pid) {
-				console.log("child process terminated due to receipt of signal " + signal);
-				if (cb) cb();
-				pid = undefined;
-			}
-		});
-
-		pid.on("close", () => {
-			if (pid) {
-				if (cb) cb();
-				pid = undefined;
-			}
-		});
-
-		pid.kill("SIGTERM");
-	}
-}
-
-function _stopController() {
-	if (objects) {
-		objects.destroy();
-		objects = null;
-	}
-	if (states) {
-		states.destroy();
-		states = null;
-	}
-}
-
-function stopController(cb) {
-	let timeout;
-	if (objects) {
-		console.log("Set system.adapter." + pkg.name + ".0");
-		objects.setObject("system.adapter." + pkg.name + ".0", {
-			common:{
-				enabled: false
-			}
-		});
-	}
-
-	stopAdapter(function () {
-		if (timeout) {
-			clearTimeout(timeout);
-			timeout = null;
-		}
-
-		_stopController();
-
-		if (cb) {
-			cb(true);
-			cb = null;
-		}
-	});
-
-	timeout = setTimeout(function () {
-		timeout = null;
-		console.log("child process NOT terminated");
-
-		_stopController();
-
-		if (cb) {
-			cb(false);
-			cb = null;
-		}
-		pid = undefined;
-	}, 5000);
-}
-
-// Setup the adapter
-function setAdapterConfig(common, native, instance) {
-	const objects = JSON.parse(fs.readFileSync(rootDir + "tmp/" + appName + "-data/objects.json").toString());
-	const id = "system.adapter." + adapterName.split(".").pop() + "." + (instance || 0);
-	if (common) objects[id].common = common;
-	if (native) objects[id].native = native;
-	fs.writeFileSync(rootDir + "tmp/" + appName + "-data/objects.json", JSON.stringify(objects));
-}
-
-// Read config of the adapter
-function getAdapterConfig(instance) {
-	const objects = JSON.parse(fs.readFileSync(rootDir + "tmp/" + appName + "-data/objects.json").toString());
-	const id      = "system.adapter." + adapterName.split(".").pop() + "." + (instance || 0);
-	return objects[id];
-}
-
-if (typeof module !== undefined && module.parent) {
-	module.exports.getAdapterConfig = getAdapterConfig;
-	module.exports.setAdapterConfig = setAdapterConfig;
-	module.exports.startController  = startController;
-	module.exports.stopController   = stopController;
-	module.exports.setupController  = setupController;
-	module.exports.stopAdapter      = stopAdapter;
-	module.exports.startAdapter     = startAdapter;
-	module.exports.installAdapter   = installAdapter;
-	module.exports.appName          = appName;
-	module.exports.adapterName      = adapterName;
-	module.exports.adapterStarted   = adapterStarted;
-}
-`;
-    return template.trim();
+    return createAdapter_1.readFile("setup.raw.js", __dirname);
 });
diff --git a/src/templates/test/lib/setup.js.ts b/build/templates/test/lib/setup.raw.js
similarity index 98%
rename from src/templates/test/lib/setup.js.ts
rename to build/templates/test/lib/setup.raw.js
index 6bbd454..9cf740c 100644
--- a/src/templates/test/lib/setup.js.ts
+++ b/build/templates/test/lib/setup.raw.js
@@ -1,8 +1,3 @@
-import { TemplateFunction } from "../../../lib/createAdapter";
-
-export = (answers => {
-
-	const template = `
 "use strict";
 
 const fs            = require("fs");
@@ -17,7 +12,7 @@ pkg.main = pkg.main || "main.js";
 let adapterStarted = false;
 
 function getAppName() {
-	const parts = __dirname.replace(/\\\\/g, "/").split("/");
+	const parts = __dirname.replace(/\\/g, "/").split("/");
 	return parts[parts.length - 3].split(".")[0];
 }
 
@@ -729,6 +724,3 @@ if (typeof module !== undefined && module.parent) {
 	module.exports.adapterName      = adapterName;
 	module.exports.adapterStarted   = adapterStarted;
 }
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/test/testPackageFiles.js.js b/build/templates/test/testPackageFiles.js.js
index 6b96bfb..9aa7f42 100644
--- a/build/templates/test/testPackageFiles.js.js
+++ b/build/templates/test/testPackageFiles.js.js
@@ -1,92 +1,5 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
-    const template = `
-"use strict";
-
-const expect = require("chai").expect;
-const fs     = require("fs");
-
-describe("Test package.json and io-package.json", function() {
-	it("Test package files", function (done) {
-		console.log();
-
-		const ioPackage = require("../io-package.json");
-		const npmPackage = require("../package.json");
-
-		expect(ioPackage).to.be.an("object");
-		expect(npmPackage).to.be.an("object");
-
-		expect(ioPackage.common.version, "ERROR: Version number in io-package.json needs to exist").to.exist;
-		expect(npmPackage.version, "ERROR: Version number in package.json needs to exist").to.exist;
-
-		expect(ioPackage.common.version, "ERROR: Version numbers in package.json and io-package.json needs to match").to.be.equal(npmPackage.version);
-
-		if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
-			console.log("WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!");
-			console.log();
-		}
-
-		expect(npmPackage.author, "ERROR: Author in package.json needs to exist").to.exist;
-		expect(ioPackage.common.authors, "ERROR: Authors in io-package.json needs to exist").to.exist;
-
-		if (ioPackage.common.name.indexOf("template") !== 0) {
-			if (Array.isArray(ioPackage.common.authors)) {
-				expect(ioPackage.common.authors.length, "ERROR: Author in io-package.json needs to be set").to.not.be.equal(0);
-				if (ioPackage.common.authors.length === 1) {
-					expect(ioPackage.common.authors[0], "ERROR: Author in io-package.json needs to be a real name").to.not.be.equal("my Name <my@email.com>");
-				}
-			}
-			else {
-				expect(ioPackage.common.authors, "ERROR: Author in io-package.json needs to be a real name").to.not.be.equal("my Name <my@email.com>");
-			}
-		}
-		else {
-			console.log("WARNING: Testing for set authors field in io-package skipped because template adapter");
-			console.log();
-		}
-		expect(fs.existsSync(__dirname + "/../README.md"), "ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.").to.be.true;
-		if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== "object") {
-			console.log("WARNING: titleLang is not existing in io-package.json. Please add");
-			console.log();
-		}
-		if (
-			ioPackage.common.title.indexOf("iobroker") !== -1 ||
-			ioPackage.common.title.indexOf("ioBroker") !== -1 ||
-			ioPackage.common.title.indexOf("adapter") !== -1 ||
-			ioPackage.common.title.indexOf("Adapter") !== -1
-		) {
-			console.log("WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.");
-			console.log();
-		}
-
-		if (ioPackage.common.name.indexOf("vis-") !== 0) {
-			if (!ioPackage.common.materialize || !fs.existsSync(__dirname + "/../admin/index_m.html") || !fs.existsSync(__dirname + "/../gulpfile.js")) {
-				console.log("WARNING: Admin3 support is missing! Please add it");
-				console.log();
-			}
-			if (ioPackage.common.materialize) {
-				expect(fs.existsSync(__dirname + "/../admin/index_m.html"), "Admin3 support is enabled in io-package.json, but index_m.html is missing!").to.be.true;
-			}
-		}
-
-		const licenseFileExists = fs.existsSync(__dirname + "/../LICENSE");
-		const fileContentReadme = fs.readFileSync(__dirname + "/../README.md", "utf8");
-		if (fileContentReadme.indexOf("## Changelog") === -1) {
-			console.log("Warning: The README.md should have a section ## Changelog");
-			console.log();
-		}
-		expect((licenseFileExists || fileContentReadme.indexOf("## License") !== -1), "A LICENSE must exist as LICENSE file or as part of the README.md").to.be.true;
-		if (!licenseFileExists) {
-			console.log("Warning: The License should also exist as LICENSE file");
-			console.log();
-		}
-		if (fileContentReadme.indexOf("## License") === -1) {
-			console.log("Warning: The README.md should also have a section ## License to be shown in Admin3");
-			console.log();
-		}
-		done();
-	});
-});
-`;
-    return template.trim();
+    return createAdapter_1.readFile("testPackageFiles.raw.js", __dirname);
 });
diff --git a/src/templates/test/testPackageFiles.js.ts b/build/templates/test/testPackageFiles.raw.js
similarity index 96%
rename from src/templates/test/testPackageFiles.js.ts
rename to build/templates/test/testPackageFiles.raw.js
index ff6058c..51cc9a8 100644
--- a/src/templates/test/testPackageFiles.js.ts
+++ b/build/templates/test/testPackageFiles.raw.js
@@ -1,8 +1,3 @@
-import { TemplateFunction } from "../../lib/createAdapter";
-
-export = (answers => {
-
-	const template = `
 "use strict";
 
 const expect = require("chai").expect;
@@ -89,6 +84,3 @@ describe("Test package.json and io-package.json", function() {
 		done();
 	});
 });
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/test/testStartup.js.js b/build/templates/test/testStartup.js.js
index 8929f94..4804fd5 100644
--- a/build/templates/test/testStartup.js.js
+++ b/build/templates/test/testStartup.js.js
@@ -1,145 +1,5 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
-    const template = `
-"use strict";
-
-const expect = require("chai").expect;
-const setup  = require("./lib/setup");
-
-let objects = null;
-let states  = null;
-let onStateChanged = null;
-// let onObjectChanged = null;
-let sendToID = 1;
-
-const adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf(".")+1);
-
-function checkConnectionOfAdapter(cb, counter) {
-	counter = counter || 0;
-	console.log("Try check #" + counter);
-	if (counter > 30) {
-		if (cb) cb("Cannot check connection");
-		return;
-	}
-
-	states.getState("system.adapter." + adapterShortName + ".0.alive", function (err, state) {
-		if (err) console.error(err);
-		if (state && state.val) {
-			if (cb) cb();
-		} else {
-			setTimeout(function () {
-				checkConnectionOfAdapter(cb, counter + 1);
-			}, 1000);
-		}
-	});
-}
-
-function checkValueOfState(id, value, cb, counter) {
-	counter = counter || 0;
-	if (counter > 20) {
-		if (cb) cb("Cannot check value Of State " + id);
-		return;
-	}
-
-	states.getState(id, function (err, state) {
-		if (err) console.error(err);
-		if (value === null && !state) {
-			if (cb) cb();
-		} else
-		if (state && (value === undefined || state.val === value)) {
-			if (cb) cb();
-		} else {
-			setTimeout(function () {
-				checkValueOfState(id, value, cb, counter + 1);
-			}, 500);
-		}
-	});
-}
-
-function sendTo(target, command, message, callback) {
-	onStateChanged = function (id, state) {
-		if (id === "messagebox.system.adapter.test.0") {
-			callback(state.message);
-		}
-	};
-
-	states.pushMessage("system.adapter." + target, {
-		command:    command,
-		message:    message,
-		from:       "system.adapter.test.0",
-		callback: {
-			message: message,
-			id:      sendToID++,
-			ack:     false,
-			time:    (new Date()).getTime()
-		}
-	});
-}
-
-describe("Test " + adapterShortName + " adapter", function() {
-	before("Test " + adapterShortName + " adapter: Start js-controller", function (_done) {
-		this.timeout(600000); // because of first install from npm
-
-		setup.setupController(function () {
-			const config = setup.getAdapterConfig();
-			// enable adapter
-			config.common.enabled  = true;
-			config.common.loglevel = "debug";
-
-			//config.native.dbtype   = 'sqlite';
-
-			setup.setAdapterConfig(config.common, config.native);
-
-			setup.startController(true, (id, obj) => { }, function (id, state) {
-				if (onStateChanged) onStateChanged(id, state);
-			},
-			function (_objects, _states) {
-				objects = _objects;
-				states  = _states;
-				_done();
-			});
-		});
-	});
-
-/*
-	ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
-*/
-	it("Test " + adapterShortName + " adapter: Check if adapter started", function (done) {
-		this.timeout(60000);
-		checkConnectionOfAdapter(function (res) {
-			if (res) console.log(res);
-			expect(res).not.to.be.equal("Cannot check connection");
-			objects.setObject("system.adapter.test.0", {
-				common: {
-
-				},
-				type: "instance"
-			},
-			function () {
-				states.subscribeMessage("system.adapter.test.0");
-				done();
-			});
-		});
-	});
-
-/*
-	PUT YOUR OWN TESTS HERE USING
-	it('Testname', function ( done) {
-		...
-	});
-
-	You can also use "sendTo" method to send messages to the started adapter
-*/
-
-	after("Test " + adapterShortName + " adapter: Stop js-controller", function (done) {
-		this.timeout(10000);
-
-		setup.stopController(function (normalTerminated) {
-			console.log("Adapter normal terminated: " + normalTerminated);
-			done();
-		});
-	});
-});
-`;
-    return template.trim();
+    return createAdapter_1.readFile("testStartup.raw.js", __dirname);
 });
diff --git a/src/templates/test/testStartup.js.ts b/build/templates/test/testStartup.raw.js
similarity index 95%
rename from src/templates/test/testStartup.js.ts
rename to build/templates/test/testStartup.raw.js
index fef8ee4..89e05d6 100644
--- a/src/templates/test/testStartup.js.ts
+++ b/build/templates/test/testStartup.raw.js
@@ -1,8 +1,3 @@
-import { TemplateFunction } from "../../lib/createAdapter";
-
-export = (answers => {
-
-	const template = `
 "use strict";
 
 const expect = require("chai").expect;
@@ -142,6 +137,3 @@ describe("Test " + adapterShortName + " adapter", function() {
 		});
 	});
 });
-`;
-	return template.trim();
-}) as TemplateFunction;
diff --git a/build/templates/test/tsconfig.json.js b/build/templates/test/tsconfig.json.js
index ff46260..1f5ee41 100644
--- a/build/templates/test/tsconfig.json.js
+++ b/build/templates/test/tsconfig.json.js
@@ -1,18 +1,9 @@
 "use strict";
+const createAdapter_1 = require("../../src/lib/createAdapter");
 module.exports = (answers => {
     const useTypeScript = answers.language === "TypeScript";
     const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
     if (!useTypeScript && !useTypeChecking)
         return;
-    const template = `
-{
-	"extends": "../tsconfig.json",
-	"compilerOptions": {
-		"noImplicitAny": false
-	},
-	"include": [
-		"./**/*.js"
-	]
-}`;
-    return template.trim();
+    return createAdapter_1.readFile("tsconfig.raw.json", __dirname);
 });
diff --git a/build/templates/test/tsconfig.raw.json b/build/templates/test/tsconfig.raw.json
new file mode 100644
index 0000000..3ebc81b
--- /dev/null
+++ b/build/templates/test/tsconfig.raw.json
@@ -0,0 +1,9 @@
+{
+	"extends": "../tsconfig.json",
+	"compilerOptions": {
+		"noImplicitAny": false
+	},
+	"include": [
+		"./**/*.js"
+	]
+}
diff --git a/templates/main.test.js.ts b/templates/main.test.js.ts
new file mode 100644
index 0000000..231bf09
--- /dev/null
+++ b/templates/main.test.js.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../src/lib/createAdapter";
+
+export = (answers => {
+
+	const useJavaScript = answers.language === "JavaScript";
+	if (!useJavaScript) return;
+
+	return readFile("main.test.raw.js", __dirname);
+}) as TemplateFunction;
diff --git a/templates/main.test.raw.js b/templates/main.test.raw.js
new file mode 100644
index 0000000..307b4c6
--- /dev/null
+++ b/templates/main.test.raw.js
@@ -0,0 +1,30 @@
+"use strict";
+
+/**
+ * This is a dummy TypeScript test file using chai and mocha
+ *
+ * It's automatically excluded from npm and its build output is excluded from both git and npm.
+ * It is advised to test all your modules with accompanying *.test.js-files
+ */
+
+// tslint:disable:no-unused-expression
+
+const { expect } = require("chai");
+// import { functionToTest } from "./moduleToTest";
+
+describe("module to test => function to test", () => {
+	// initializing logic
+	const expected = 5;
+
+	it(`should return ${expected}`, () => {
+		const result = 5;
+		// assign result a value from functionToTest
+		expect(result).to.equal(expected);
+		// or using the should() syntax
+		result.should.equal(expected);
+	});
+	// ... more tests => it
+
+});
+
+// ... more test suites => describe
diff --git a/templates/src/main.test.raw.ts b/templates/src/main.test.raw.ts
new file mode 100644
index 0000000..513a325
--- /dev/null
+++ b/templates/src/main.test.raw.ts
@@ -0,0 +1,28 @@
+/**
+ * This is a dummy TypeScript test file using chai and mocha
+ *
+ * It's automatically excluded from npm and its build output is excluded from both git and npm.
+ * It is advised to test all your modules with accompanying *.test.ts-files
+ */
+
+// tslint:disable:no-unused-expression
+
+import { expect } from "chai";
+// import { functionToTest } from "./moduleToTest";
+
+describe("module to test => function to test", () => {
+	// initializing logic
+	const expected = 5;
+
+	it(`should return ${expected}`, () => {
+		const result = 5;
+		// assign result a value from functionToTest
+		expect(result).to.equal(expected);
+		// or using the should() syntax
+		result.should.equal(expected);
+	});
+	// ... more tests => it
+
+});
+
+// ... more test suites => describe
diff --git a/templates/src/main.test.ts.ts b/templates/src/main.test.ts.ts
new file mode 100644
index 0000000..d7f2f65
--- /dev/null
+++ b/templates/src/main.test.ts.ts
@@ -0,0 +1,9 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+export = (answers => {
+
+	const useTypeScript = answers.language === "TypeScript";
+	if (!useTypeScript) return;
+
+	return readFile("main.test.raw.ts", __dirname);
+}) as TemplateFunction;
diff --git a/templates/test/lib/setup.js.ts b/templates/test/lib/setup.js.ts
new file mode 100644
index 0000000..c21d29a
--- /dev/null
+++ b/templates/test/lib/setup.js.ts
@@ -0,0 +1,5 @@
+import { readFile, TemplateFunction } from "../../../src/lib/createAdapter";
+
+export = (answers => {
+	return readFile("setup.raw.js", __dirname);
+}) as TemplateFunction;
diff --git a/templates/test/lib/setup.raw.js b/templates/test/lib/setup.raw.js
new file mode 100644
index 0000000..9cf740c
--- /dev/null
+++ b/templates/test/lib/setup.raw.js
@@ -0,0 +1,726 @@
+"use strict";
+
+const fs            = require("fs");
+const path          = require("path");
+const child_process = require("child_process");
+const rootDir       = path.normalize(__dirname + "/../../");
+const adapterName   = path.basename(rootDir);
+const pkg           = require(rootDir + "package.json");
+const debug         = typeof v8debug === "object";
+pkg.main = pkg.main || "main.js";
+
+let adapterStarted = false;
+
+function getAppName() {
+	const parts = __dirname.replace(/\\/g, "/").split("/");
+	return parts[parts.length - 3].split(".")[0];
+}
+
+const appName = getAppName().toLowerCase();
+
+let objects;
+let states;
+
+/** @type {undefined | ReturnType<typeof import("child_process")["exec"]>} */
+let pid;
+
+function copyFileSync(source, target) {
+
+	let targetFile = target;
+
+	//if target is a directory a new file with the same name will be created
+	if (fs.existsSync(target)) {
+		if ( fs.lstatSync( target ).isDirectory() ) {
+			targetFile = path.join(target, path.basename(source));
+		}
+	}
+
+	try {
+		fs.writeFileSync(targetFile, fs.readFileSync(source));
+	}
+	catch (err) {
+		console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
+	}
+}
+
+function copyFolderRecursiveSync(source, target, ignore) {
+	let files = [];
+
+	let base = path.basename(source);
+	if (base === adapterName) {
+		base = pkg.name;
+	}
+	//check if folder needs to be created or integrated
+	const targetFolder = path.join(target, base);
+	if (!fs.existsSync(targetFolder)) {
+		fs.mkdirSync(targetFolder);
+	}
+
+	//copy
+	if (fs.lstatSync(source).isDirectory()) {
+		files = fs.readdirSync(source);
+		files.forEach(function (file) {
+			if (ignore && ignore.indexOf(file) !== -1) {
+				return;
+			}
+
+			const curSource = path.join(source, file);
+			const curTarget = path.join(targetFolder, file);
+			if (fs.lstatSync(curSource).isDirectory()) {
+				// ignore grunt files
+				if (file.indexOf("grunt") !== -1) return;
+				if (file === "chai") return;
+				if (file === "mocha") return;
+				copyFolderRecursiveSync(curSource, targetFolder, ignore);
+			} else {
+				copyFileSync(curSource, curTarget);
+			}
+		});
+	}
+}
+
+if (!fs.existsSync(rootDir + "tmp")) {
+	fs.mkdirSync(rootDir + "tmp");
+}
+
+function storeOriginalFiles() {
+	console.log("Store original files...");
+	const dataDir = rootDir + "tmp/" + appName + "-data/";
+
+	let f = fs.readFileSync(dataDir + "objects.json");
+	const objects = JSON.parse(f.toString());
+	if (objects["system.adapter.admin.0"] && objects["system.adapter.admin.0"].common) {
+		objects["system.adapter.admin.0"].common.enabled = false;
+	}
+	if (objects["system.adapter.admin.1"] && objects["system.adapter.admin.1"].common) {
+		objects["system.adapter.admin.1"].common.enabled = false;
+	}
+
+	fs.writeFileSync(dataDir + "objects.json.original", JSON.stringify(objects));
+	try {
+		f = fs.readFileSync(dataDir + "states.json");
+		fs.writeFileSync(dataDir + "states.json.original", f);
+	}
+	catch (err) {
+		console.log("no states.json found - ignore");
+	}
+}
+
+function restoreOriginalFiles() {
+	console.log("restoreOriginalFiles...");
+	const dataDir = rootDir + "tmp/" + appName + "-data/";
+
+	let f = fs.readFileSync(dataDir + "objects.json.original");
+	fs.writeFileSync(dataDir + "objects.json", f);
+	try {
+		f = fs.readFileSync(dataDir + "states.json.original");
+		fs.writeFileSync(dataDir + "states.json", f);
+	}
+	catch (err) {
+		console.log("no states.json.original found - ignore");
+	}
+
+}
+
+function checkIsAdapterInstalled(cb, counter, customName) {
+	customName = customName || pkg.name.split(".").pop();
+	counter = counter || 0;
+	const dataDir = rootDir + "tmp/" + appName + "-data/";
+	console.log("checkIsAdapterInstalled...");
+
+	try {
+		const f = fs.readFileSync(dataDir + "objects.json");
+		const objects = JSON.parse(f.toString());
+		if (objects["system.adapter." + customName + ".0"]) {
+			console.log("checkIsAdapterInstalled: ready!");
+			setTimeout(function () {
+				if (cb) cb();
+			}, 100);
+			return;
+		} else {
+			console.warn("checkIsAdapterInstalled: still not ready");
+		}
+	} catch (err) {
+		// Don't care
+	}
+
+	if (counter > 20) {
+		console.error("checkIsAdapterInstalled: Cannot install!");
+		if (cb) cb("Cannot install");
+	} else {
+		console.log("checkIsAdapterInstalled: wait...");
+		setTimeout(function() {
+			checkIsAdapterInstalled(cb, counter + 1);
+		}, 1000);
+	}
+}
+
+function checkIsControllerInstalled(cb, counter) {
+	counter = counter || 0;
+	const dataDir = rootDir + "tmp/" + appName + "-data/";
+
+	console.log("checkIsControllerInstalled...");
+	try {
+		const f = fs.readFileSync(dataDir + "objects.json");
+		const objects = JSON.parse(f.toString());
+		if (objects["system.adapter.admin.0"]) {
+			console.log("checkIsControllerInstalled: installed!");
+			setTimeout(function () {
+				if (cb) cb();
+			}, 100);
+			return;
+		}
+	} catch (err) {
+		// Don't care
+	}
+
+	if (counter > 20) {
+		console.log("checkIsControllerInstalled: Cannot install!");
+		if (cb) cb("Cannot install");
+	} else {
+		console.log("checkIsControllerInstalled: wait...");
+		setTimeout(function() {
+			checkIsControllerInstalled(cb, counter + 1);
+		}, 1000);
+	}
+}
+
+function installAdapter(customName, cb) {
+	if (typeof customName === "function") {
+		cb = customName;
+		customName = null;
+	}
+	customName = customName || pkg.name.split(".").pop();
+	console.log("Install adapter...");
+	const startFile = "node_modules/" + appName + ".js-controller/" + appName + ".js";
+	// make first install
+	if (debug) {
+		child_process.execSync("node " + startFile + " add " + customName + " --enabled false", {
+			cwd:   rootDir + "tmp",
+			stdio: [0, 1, 2]
+		});
+		checkIsAdapterInstalled(function (error) {
+			if (error) console.error(error);
+			console.log("Adapter installed.");
+			if (cb) cb();
+		});
+	} else {
+		// add controller
+		const _pid = child_process.fork(startFile, ["add", customName, "--enabled", "false"], {
+			cwd:   rootDir + "tmp",
+			stdio: [0, 1, 2, "ipc"]
+		});
+
+		waitForEnd(_pid, function () {
+			checkIsAdapterInstalled(function (error) {
+				if (error) console.error(error);
+				console.log("Adapter installed.");
+				if (cb) cb();
+			});
+		});
+	}
+}
+
+function waitForEnd(_pid, cb) {
+	if (!_pid) {
+		cb(-1, -1);
+		return;
+	}
+	_pid.on("exit", function (code, signal) {
+		if (_pid) {
+			_pid = null;
+			cb(code, signal);
+		}
+	});
+	_pid.on("close", function (code, signal) {
+		if (_pid) {
+			_pid = null;
+			cb(code, signal);
+		}
+	});
+}
+
+function installJsController(cb) {
+	console.log("installJsController...");
+	if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller") ||
+		!fs.existsSync(rootDir + "tmp/" + appName + "-data")) {
+		// try to detect appName.js-controller in node_modules/appName.js-controller
+		// travis CI installs js-controller into node_modules
+		if (fs.existsSync(rootDir + "node_modules/" + appName + ".js-controller")) {
+			console.log('installJsController: no js-controller => copy it from "' + rootDir + "node_modules/" + appName + '.js-controller"');
+			// copy all
+			// stop controller
+			console.log("Stop controller if running...");
+			let _pid;
+			if (debug) {
+				// start controller
+				_pid = child_process.exec("node " + appName + ".js stop", {
+					cwd: rootDir + "node_modules/" + appName + ".js-controller",
+					stdio: [0, 1, 2]
+				});
+			} else {
+				_pid = child_process.fork(appName + ".js", ["stop"], {
+					cwd:   rootDir + "node_modules/" + appName + ".js-controller",
+					stdio: [0, 1, 2, "ipc"]
+				});
+			}
+
+			waitForEnd(_pid, function () {
+				// copy all files into
+				if (!fs.existsSync(rootDir + "tmp")) fs.mkdirSync(rootDir + "tmp");
+				if (!fs.existsSync(rootDir + "tmp/node_modules")) fs.mkdirSync(rootDir + "tmp/node_modules");
+
+				if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller")){
+					console.log("Copy js-controller...");
+					copyFolderRecursiveSync(rootDir + "node_modules/" + appName + ".js-controller", rootDir + "tmp/node_modules/");
+				}
+
+				console.log("Setup js-controller...");
+				let __pid;
+				if (debug) {
+					// start controller
+					_pid = child_process.exec("node " + appName + ".js setup first --console", {
+						cwd: rootDir + "tmp/node_modules/" + appName + ".js-controller",
+						stdio: [0, 1, 2]
+					});
+				} else {
+					__pid = child_process.fork(appName + ".js", ["setup", "first", "--console"], {
+						cwd:   rootDir + "tmp/node_modules/" + appName + ".js-controller",
+						stdio: [0, 1, 2, "ipc"]
+					});
+				}
+				waitForEnd(__pid, function () {
+					checkIsControllerInstalled(function () {
+						// change ports for object and state DBs
+						const config = require(rootDir + "tmp/" + appName + "-data/" + appName + ".json");
+						config.objects.port = 19001;
+						config.states.port  = 19000;
+						fs.writeFileSync(rootDir + "tmp/" + appName + "-data/" + appName + ".json", JSON.stringify(config, null, 2));
+						console.log("Setup finished.");
+
+						copyAdapterToController();
+
+						installAdapter(function () {
+							storeOriginalFiles();
+							if (cb) cb(true);
+						});
+					});
+				});
+			});
+		} else {
+			// check if port 9000 is free, else admin adapter will be added to running instance
+			const Socket = require("net").Socket;
+			const client = new Socket();
+			client.connect(9000, "127.0.0.1", function() {
+				console.error("Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.");
+				process.exit(0);
+			});
+
+			setTimeout(function () {
+				client.destroy();
+				if (!fs.existsSync(rootDir + "tmp/node_modules/" + appName + ".js-controller")) {
+					console.log("installJsController: no js-controller => install from git");
+
+					child_process.execSync("npm install https://github.com/" + appName + "/" + appName + ".js-controller/tarball/master --prefix ./  --production", {
+						cwd:   rootDir + "tmp/",
+						stdio: [0, 1, 2]
+					});
+				} else {
+					console.log("Setup js-controller...");
+					if (debug) {
+						// start controller
+						child_process.exec("node " + appName + ".js setup first", {
+							cwd: rootDir + "tmp/node_modules/" + appName + ".js-controller",
+							stdio: [0, 1, 2]
+						});
+					} else {
+						child_process.fork(appName + ".js", ["setup", "first"], {
+							cwd:   rootDir + "tmp/node_modules/" + appName + ".js-controller",
+							stdio: [0, 1, 2, "ipc"]
+						});
+					}
+				}
+
+				// let npm install admin and run setup
+				checkIsControllerInstalled(function () {
+					let _pid;
+
+					if (fs.existsSync(rootDir + "node_modules/" + appName + ".js-controller/" + appName + ".js")) {
+						_pid = child_process.fork(appName + ".js", ["stop"], {
+							cwd:   rootDir + "node_modules/" + appName + ".js-controller",
+							stdio: [0, 1, 2, "ipc"]
+						});
+					}
+
+					waitForEnd(_pid, function () {
+						// change ports for object and state DBs
+						const config = require(rootDir + "tmp/" + appName + "-data/" + appName + ".json");
+						config.objects.port = 19001;
+						config.states.port  = 19000;
+						fs.writeFileSync(rootDir + "tmp/" + appName + "-data/" + appName + ".json", JSON.stringify(config, null, 2));
+
+						copyAdapterToController();
+
+						installAdapter(function () {
+							storeOriginalFiles();
+							if (cb) cb(true);
+						});
+					});
+				});
+			}, 1000);
+		}
+	} else {
+		setTimeout(function () {
+			console.log("installJsController: js-controller installed");
+			if (cb) cb(false);
+		}, 0);
+	}
+}
+
+function copyAdapterToController() {
+	console.log("Copy adapter...");
+	// Copy adapter to tmp/node_modules/appName.adapter
+	copyFolderRecursiveSync(rootDir, rootDir + "tmp/node_modules/", [".idea", "test", "tmp", ".git", appName + ".js-controller"]);
+	console.log("Adapter copied.");
+}
+
+function clearControllerLog() {
+	const dirPath = rootDir + "tmp/log";
+	let files;
+	try {
+		if (fs.existsSync(dirPath)) {
+			console.log("Clear controller log...");
+			files = fs.readdirSync(dirPath);
+		} else {
+			console.log("Create controller log directory...");
+			files = [];
+			fs.mkdirSync(dirPath);
+		}
+	} catch(e) {
+		console.error('Cannot read "' + dirPath + '"');
+		return;
+	}
+	if (files.length > 0) {
+		try {
+			for (let i = 0; i < files.length; i++) {
+				const filePath = dirPath + "/" + files[i];
+				fs.unlinkSync(filePath);
+			}
+			console.log("Controller log cleared");
+		} catch (err) {
+			console.error("cannot clear log: " + err);
+		}
+	}
+}
+
+function clearDB() {
+	const dirPath = rootDir + "tmp/iobroker-data/sqlite";
+	let files;
+	try {
+		if (fs.existsSync(dirPath)) {
+			console.log("Clear sqlite DB...");
+			files = fs.readdirSync(dirPath);
+		} else {
+			console.log("Create controller log directory...");
+			files = [];
+			fs.mkdirSync(dirPath);
+		}
+	} catch(e) {
+		console.error('Cannot read "' + dirPath + '"');
+		return;
+	}
+	if (files.length > 0) {
+		try {
+			for (let i = 0; i < files.length; i++) {
+				const filePath = dirPath + "/" + files[i];
+				fs.unlinkSync(filePath);
+			}
+			console.log("Clear sqlite DB");
+		} catch (err) {
+			console.error("cannot clear DB: " + err);
+		}
+	}
+}
+
+function setupController(cb) {
+	installJsController(function (isInited) {
+		clearControllerLog();
+		clearDB();
+
+		if (!isInited) {
+			restoreOriginalFiles();
+			copyAdapterToController();
+		}
+		// read system.config object
+		const dataDir = rootDir + "tmp/" + appName + "-data/";
+
+		let objs;
+		try {
+			objs = JSON.parse(fs.readFileSync(dataDir + "objects.json", "utf8"));
+		}
+		catch (e) {
+			console.log("ERROR reading/parsing system configuration. Ignore");
+			objs = {"system.config": {}};
+		}
+		if (!objs || !objs["system.config"]) {
+			objs = {"system.config": {}};
+		}
+
+		if (cb) cb(objs["system.config"]);
+	});
+}
+
+function startAdapter(objects, states, callback) {
+	if (adapterStarted) {
+		console.log("Adapter already started ...");
+		if (callback) callback(objects, states);
+		return;
+	}
+	adapterStarted = true;
+	console.log("startAdapter...");
+	if (fs.existsSync(rootDir + "tmp/node_modules/" + pkg.name + "/" + pkg.main)) {
+		try {
+			if (debug) {
+				// start controller
+				pid = child_process.exec("node node_modules/" + pkg.name + "/" + pkg.main + " --console silly", {
+					cwd: rootDir + "tmp",
+					stdio: [0, 1, 2]
+				});
+			} else {
+				// start controller
+				pid = child_process.fork("node_modules/" + pkg.name + "/" + pkg.main, ["--console", "silly"], {
+					cwd:   rootDir + "tmp",
+					stdio: [0, 1, 2, "ipc"]
+				});
+			}
+		} catch (error) {
+			console.error(JSON.stringify(error));
+		}
+	} else {
+		console.error("Cannot find: " + rootDir + "tmp/node_modules/" + pkg.name + "/" + pkg.main);
+	}
+	if (callback) callback(objects, states);
+}
+
+function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
+	if (typeof isStartAdapter === "function") {
+		callback = onStateChange;
+		onStateChange = onObjectChange;
+		onObjectChange = isStartAdapter;
+		isStartAdapter = true;
+	}
+
+	if (onStateChange === undefined) {
+		callback  = onObjectChange;
+		onObjectChange = undefined;
+	}
+
+	if (pid) {
+		console.error("Controller is already started!");
+	} else {
+		console.log("startController...");
+		adapterStarted = false;
+		let isObjectConnected;
+		let isStatesConnected;
+
+		const Objects = require(rootDir + "tmp/node_modules/" + appName + ".js-controller/lib/objects/objectsInMemServer");
+		objects = new Objects({
+			connection: {
+				"type" : "file",
+				"host" : "127.0.0.1",
+				"port" : 19001,
+				"user" : "",
+				"pass" : "",
+				"noFileCache": false,
+				"connectTimeout": 2000
+			},
+			logger: {
+				silly: function (msg) {
+					console.log(msg);
+				},
+				debug: function (msg) {
+					console.log(msg);
+				},
+				info: function (msg) {
+					console.log(msg);
+				},
+				warn: function (msg) {
+					console.warn(msg);
+				},
+				error: function (msg) {
+					console.error(msg);
+				}
+			},
+			connected: function () {
+				isObjectConnected = true;
+				if (isStatesConnected) {
+					console.log("startController: started!");
+					if (isStartAdapter) {
+						startAdapter(objects, states, callback);
+					} else {
+						if (callback) {
+							callback(objects, states);
+							callback = null;
+						}
+					}
+				}
+			},
+			change: onObjectChange
+		});
+
+		// Just open in memory DB itself
+		const States = require(rootDir + "tmp/node_modules/" + appName + ".js-controller/lib/states/statesInMemServer");
+		states = new States({
+			connection: {
+				type: "file",
+				host: "127.0.0.1",
+				port: 19000,
+				options: {
+					auth_pass: null,
+					retry_max_delay: 15000
+				}
+			},
+			logger: {
+				silly: function (msg) {
+					console.log(msg);
+				},
+				debug: function (msg) {
+					console.log(msg);
+				},
+				info: function (msg) {
+					console.log(msg);
+				},
+				warn: function (msg) {
+					console.log(msg);
+				},
+				error: function (msg) {
+					console.log(msg);
+				}
+			},
+			connected: function () {
+				isStatesConnected = true;
+				if (isObjectConnected) {
+					console.log("startController: started!!");
+					if (isStartAdapter) {
+						startAdapter(objects, states, callback);
+					} else {
+						if (callback) {
+							callback(objects, states);
+							callback = null;
+						}
+					}
+				}
+			},
+			change: onStateChange
+		});
+	}
+}
+
+function stopAdapter(cb) {
+	if (!pid) {
+		console.error("Controller is not running!");
+		if (cb) {
+			setTimeout(function () {
+				cb(false);
+			}, 0);
+		}
+	} else {
+		adapterStarted = false;
+		pid.on("exit", function (code, signal) {
+			if (pid) {
+				console.log("child process terminated due to receipt of signal " + signal);
+				if (cb) cb();
+				pid = undefined;
+			}
+		});
+
+		pid.on("close", () => {
+			if (pid) {
+				if (cb) cb();
+				pid = undefined;
+			}
+		});
+
+		pid.kill("SIGTERM");
+	}
+}
+
+function _stopController() {
+	if (objects) {
+		objects.destroy();
+		objects = null;
+	}
+	if (states) {
+		states.destroy();
+		states = null;
+	}
+}
+
+function stopController(cb) {
+	let timeout;
+	if (objects) {
+		console.log("Set system.adapter." + pkg.name + ".0");
+		objects.setObject("system.adapter." + pkg.name + ".0", {
+			common:{
+				enabled: false
+			}
+		});
+	}
+
+	stopAdapter(function () {
+		if (timeout) {
+			clearTimeout(timeout);
+			timeout = null;
+		}
+
+		_stopController();
+
+		if (cb) {
+			cb(true);
+			cb = null;
+		}
+	});
+
+	timeout = setTimeout(function () {
+		timeout = null;
+		console.log("child process NOT terminated");
+
+		_stopController();
+
+		if (cb) {
+			cb(false);
+			cb = null;
+		}
+		pid = undefined;
+	}, 5000);
+}
+
+// Setup the adapter
+function setAdapterConfig(common, native, instance) {
+	const objects = JSON.parse(fs.readFileSync(rootDir + "tmp/" + appName + "-data/objects.json").toString());
+	const id = "system.adapter." + adapterName.split(".").pop() + "." + (instance || 0);
+	if (common) objects[id].common = common;
+	if (native) objects[id].native = native;
+	fs.writeFileSync(rootDir + "tmp/" + appName + "-data/objects.json", JSON.stringify(objects));
+}
+
+// Read config of the adapter
+function getAdapterConfig(instance) {
+	const objects = JSON.parse(fs.readFileSync(rootDir + "tmp/" + appName + "-data/objects.json").toString());
+	const id      = "system.adapter." + adapterName.split(".").pop() + "." + (instance || 0);
+	return objects[id];
+}
+
+if (typeof module !== undefined && module.parent) {
+	module.exports.getAdapterConfig = getAdapterConfig;
+	module.exports.setAdapterConfig = setAdapterConfig;
+	module.exports.startController  = startController;
+	module.exports.stopController   = stopController;
+	module.exports.setupController  = setupController;
+	module.exports.stopAdapter      = stopAdapter;
+	module.exports.startAdapter     = startAdapter;
+	module.exports.installAdapter   = installAdapter;
+	module.exports.appName          = appName;
+	module.exports.adapterName      = adapterName;
+	module.exports.adapterStarted   = adapterStarted;
+}
diff --git a/src/templates/test/mocha.custom.opts.ts b/templates/test/mocha.custom.opts.ts
similarity index 90%
rename from src/templates/test/mocha.custom.opts.ts
rename to templates/test/mocha.custom.opts.ts
index e1a79a8..e1cdfad 100644
--- a/src/templates/test/mocha.custom.opts.ts
+++ b/templates/test/mocha.custom.opts.ts
@@ -1,5 +1,5 @@
 import * as os from "os";
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/src/templates/test/mocha.setup.js.ts b/templates/test/mocha.setup.js.ts
similarity index 91%
rename from src/templates/test/mocha.setup.js.ts
rename to templates/test/mocha.setup.js.ts
index f241811..862cc59 100644
--- a/src/templates/test/mocha.setup.js.ts
+++ b/templates/test/mocha.setup.js.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
diff --git a/templates/test/testPackageFiles.js.ts b/templates/test/testPackageFiles.js.ts
new file mode 100644
index 0000000..e9c4914
--- /dev/null
+++ b/templates/test/testPackageFiles.js.ts
@@ -0,0 +1,5 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+export = (answers => {
+	return readFile("testPackageFiles.raw.js", __dirname);
+}) as TemplateFunction;
diff --git a/templates/test/testPackageFiles.raw.js b/templates/test/testPackageFiles.raw.js
new file mode 100644
index 0000000..51cc9a8
--- /dev/null
+++ b/templates/test/testPackageFiles.raw.js
@@ -0,0 +1,86 @@
+"use strict";
+
+const expect = require("chai").expect;
+const fs     = require("fs");
+
+describe("Test package.json and io-package.json", function() {
+	it("Test package files", function (done) {
+		console.log();
+
+		const ioPackage = require("../io-package.json");
+		const npmPackage = require("../package.json");
+
+		expect(ioPackage).to.be.an("object");
+		expect(npmPackage).to.be.an("object");
+
+		expect(ioPackage.common.version, "ERROR: Version number in io-package.json needs to exist").to.exist;
+		expect(npmPackage.version, "ERROR: Version number in package.json needs to exist").to.exist;
+
+		expect(ioPackage.common.version, "ERROR: Version numbers in package.json and io-package.json needs to match").to.be.equal(npmPackage.version);
+
+		if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
+			console.log("WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!");
+			console.log();
+		}
+
+		expect(npmPackage.author, "ERROR: Author in package.json needs to exist").to.exist;
+		expect(ioPackage.common.authors, "ERROR: Authors in io-package.json needs to exist").to.exist;
+
+		if (ioPackage.common.name.indexOf("template") !== 0) {
+			if (Array.isArray(ioPackage.common.authors)) {
+				expect(ioPackage.common.authors.length, "ERROR: Author in io-package.json needs to be set").to.not.be.equal(0);
+				if (ioPackage.common.authors.length === 1) {
+					expect(ioPackage.common.authors[0], "ERROR: Author in io-package.json needs to be a real name").to.not.be.equal("my Name <my@email.com>");
+				}
+			}
+			else {
+				expect(ioPackage.common.authors, "ERROR: Author in io-package.json needs to be a real name").to.not.be.equal("my Name <my@email.com>");
+			}
+		}
+		else {
+			console.log("WARNING: Testing for set authors field in io-package skipped because template adapter");
+			console.log();
+		}
+		expect(fs.existsSync(__dirname + "/../README.md"), "ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.").to.be.true;
+		if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== "object") {
+			console.log("WARNING: titleLang is not existing in io-package.json. Please add");
+			console.log();
+		}
+		if (
+			ioPackage.common.title.indexOf("iobroker") !== -1 ||
+			ioPackage.common.title.indexOf("ioBroker") !== -1 ||
+			ioPackage.common.title.indexOf("adapter") !== -1 ||
+			ioPackage.common.title.indexOf("Adapter") !== -1
+		) {
+			console.log("WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.");
+			console.log();
+		}
+
+		if (ioPackage.common.name.indexOf("vis-") !== 0) {
+			if (!ioPackage.common.materialize || !fs.existsSync(__dirname + "/../admin/index_m.html") || !fs.existsSync(__dirname + "/../gulpfile.js")) {
+				console.log("WARNING: Admin3 support is missing! Please add it");
+				console.log();
+			}
+			if (ioPackage.common.materialize) {
+				expect(fs.existsSync(__dirname + "/../admin/index_m.html"), "Admin3 support is enabled in io-package.json, but index_m.html is missing!").to.be.true;
+			}
+		}
+
+		const licenseFileExists = fs.existsSync(__dirname + "/../LICENSE");
+		const fileContentReadme = fs.readFileSync(__dirname + "/../README.md", "utf8");
+		if (fileContentReadme.indexOf("## Changelog") === -1) {
+			console.log("Warning: The README.md should have a section ## Changelog");
+			console.log();
+		}
+		expect((licenseFileExists || fileContentReadme.indexOf("## License") !== -1), "A LICENSE must exist as LICENSE file or as part of the README.md").to.be.true;
+		if (!licenseFileExists) {
+			console.log("Warning: The License should also exist as LICENSE file");
+			console.log();
+		}
+		if (fileContentReadme.indexOf("## License") === -1) {
+			console.log("Warning: The README.md should also have a section ## License to be shown in Admin3");
+			console.log();
+		}
+		done();
+	});
+});
diff --git a/templates/test/testStartup.js.ts b/templates/test/testStartup.js.ts
new file mode 100644
index 0000000..7c84285
--- /dev/null
+++ b/templates/test/testStartup.js.ts
@@ -0,0 +1,5 @@
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
+
+export = (answers => {
+	return readFile("testStartup.raw.js", __dirname);
+}) as TemplateFunction;
diff --git a/templates/test/testStartup.raw.js b/templates/test/testStartup.raw.js
new file mode 100644
index 0000000..89e05d6
--- /dev/null
+++ b/templates/test/testStartup.raw.js
@@ -0,0 +1,139 @@
+"use strict";
+
+const expect = require("chai").expect;
+const setup  = require("./lib/setup");
+
+let objects = null;
+let states  = null;
+let onStateChanged = null;
+// let onObjectChanged = null;
+let sendToID = 1;
+
+const adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf(".")+1);
+
+function checkConnectionOfAdapter(cb, counter) {
+	counter = counter || 0;
+	console.log("Try check #" + counter);
+	if (counter > 30) {
+		if (cb) cb("Cannot check connection");
+		return;
+	}
+
+	states.getState("system.adapter." + adapterShortName + ".0.alive", function (err, state) {
+		if (err) console.error(err);
+		if (state && state.val) {
+			if (cb) cb();
+		} else {
+			setTimeout(function () {
+				checkConnectionOfAdapter(cb, counter + 1);
+			}, 1000);
+		}
+	});
+}
+
+function checkValueOfState(id, value, cb, counter) {
+	counter = counter || 0;
+	if (counter > 20) {
+		if (cb) cb("Cannot check value Of State " + id);
+		return;
+	}
+
+	states.getState(id, function (err, state) {
+		if (err) console.error(err);
+		if (value === null && !state) {
+			if (cb) cb();
+		} else
+		if (state && (value === undefined || state.val === value)) {
+			if (cb) cb();
+		} else {
+			setTimeout(function () {
+				checkValueOfState(id, value, cb, counter + 1);
+			}, 500);
+		}
+	});
+}
+
+function sendTo(target, command, message, callback) {
+	onStateChanged = function (id, state) {
+		if (id === "messagebox.system.adapter.test.0") {
+			callback(state.message);
+		}
+	};
+
+	states.pushMessage("system.adapter." + target, {
+		command:    command,
+		message:    message,
+		from:       "system.adapter.test.0",
+		callback: {
+			message: message,
+			id:      sendToID++,
+			ack:     false,
+			time:    (new Date()).getTime()
+		}
+	});
+}
+
+describe("Test " + adapterShortName + " adapter", function() {
+	before("Test " + adapterShortName + " adapter: Start js-controller", function (_done) {
+		this.timeout(600000); // because of first install from npm
+
+		setup.setupController(function () {
+			const config = setup.getAdapterConfig();
+			// enable adapter
+			config.common.enabled  = true;
+			config.common.loglevel = "debug";
+
+			//config.native.dbtype   = 'sqlite';
+
+			setup.setAdapterConfig(config.common, config.native);
+
+			setup.startController(true, (id, obj) => { }, function (id, state) {
+				if (onStateChanged) onStateChanged(id, state);
+			},
+			function (_objects, _states) {
+				objects = _objects;
+				states  = _states;
+				_done();
+			});
+		});
+	});
+
+/*
+	ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
+*/
+	it("Test " + adapterShortName + " adapter: Check if adapter started", function (done) {
+		this.timeout(60000);
+		checkConnectionOfAdapter(function (res) {
+			if (res) console.log(res);
+			expect(res).not.to.be.equal("Cannot check connection");
+			objects.setObject("system.adapter.test.0", {
+				common: {
+
+				},
+				type: "instance"
+			},
+			function () {
+				states.subscribeMessage("system.adapter.test.0");
+				done();
+			});
+		});
+	});
+
+/*
+	PUT YOUR OWN TESTS HERE USING
+	it('Testname', function ( done) {
+		...
+	});
+
+	You can also use "sendTo" method to send messages to the started adapter
+*/
+
+	after("Test " + adapterShortName + " adapter: Stop js-controller", function (done) {
+		this.timeout(10000);
+
+		setup.stopController(function (normalTerminated) {
+			console.log("Adapter normal terminated: " + normalTerminated);
+			done();
+		});
+	});
+});
diff --git a/src/templates/test/tsconfig.json.ts b/templates/test/tsconfig.json.ts
similarity index 52%
rename from src/templates/test/tsconfig.json.ts
rename to templates/test/tsconfig.json.ts
index 43de33e..0eaf062 100644
--- a/src/templates/test/tsconfig.json.ts
+++ b/templates/test/tsconfig.json.ts
@@ -1,4 +1,4 @@
-import { TemplateFunction } from "../../lib/createAdapter";
+import { readFile, TemplateFunction } from "../../src/lib/createAdapter";
 
 export = (answers => {
 
@@ -6,15 +6,5 @@ export = (answers => {
 	const useTypeChecking = answers.tools && answers.tools.indexOf("type checking") > -1;
 	if (!useTypeScript && !useTypeChecking) return;
 
-	const template = `
-{
-	"extends": "../tsconfig.json",
-	"compilerOptions": {
-		"noImplicitAny": false
-	},
-	"include": [
-		"./**/*.js"
-	]
-}`;
-	return template.trim();
+	return readFile("tsconfig.raw.json", __dirname);
 }) as TemplateFunction;
diff --git a/templates/test/tsconfig.raw.json b/templates/test/tsconfig.raw.json
new file mode 100644
index 0000000..3ebc81b
--- /dev/null
+++ b/templates/test/tsconfig.raw.json
@@ -0,0 +1,9 @@
+{
+	"extends": "../tsconfig.json",
+	"compilerOptions": {
+		"noImplicitAny": false
+	},
+	"include": [
+		"./**/*.js"
+	]
+}
diff --git a/test/baselines/TS_SingleQuotes/src/lib/tools.ts b/test/baselines/TS_SingleQuotes/src/lib/tools.ts
index 4cf8ac0..1e44751 100644
--- a/test/baselines/TS_SingleQuotes/src/lib/tools.ts
+++ b/test/baselines/TS_SingleQuotes/src/lib/tools.ts
@@ -40,4 +40,4 @@ export async function translateText(text: string, targetLang: string): Promise<s
 		throw new Error(`Could not translate to "${targetLang}": ${e}`);
 	}
 	return text;
-}
\ No newline at end of file
+}
diff --git a/test/baselines/TS_SingleQuotes/src/main.test.ts b/test/baselines/TS_SingleQuotes/src/main.test.ts
index 9fccc5b..da2c995 100644
--- a/test/baselines/TS_SingleQuotes/src/main.test.ts
+++ b/test/baselines/TS_SingleQuotes/src/main.test.ts
@@ -25,4 +25,4 @@ describe('module to test => function to test', () => {
 
 });
 
-// ... more test suites => describe
\ No newline at end of file
+// ... more test suites => describe
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/.travis.yml b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/.travis.yml
index 69b581d..3f99e77 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/.travis.yml
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/.travis.yml
@@ -29,4 +29,4 @@ before_script:
   - 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
 script:
   - npm run test:package
-  - npm run test:iobroker
\ No newline at end of file
+  - npm run test:iobroker
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/admin.d.ts b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/admin.d.ts
index cce2aad..13444d9 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/admin.d.ts
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/admin.d.ts
@@ -1,1 +1,1 @@
-declare let systemDictionary: Record<string, Record<string, string>>;
\ No newline at end of file
+declare let systemDictionary: Record<string, Record<string, string>>;
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/style.css b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/style.css
index 1703066..703d654 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/style.css
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/style.css
@@ -7,4 +7,4 @@
     overflow: initial;
 }
 
-/* Add your styles here */
\ No newline at end of file
+/* Add your styles here */
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/tsconfig.json b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/tsconfig.json
index 4f28d86..b99d93e 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/tsconfig.json
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/admin/tsconfig.json
@@ -4,4 +4,4 @@
         "./**/*.d.ts",
         "./**/*.js"
     ]
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/lib/tools.js b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/lib/tools.js
index 3751af0..f170b13 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/lib/tools.js
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/lib/tools.js
@@ -49,4 +49,4 @@ module.exports = {
     isArray,
     isObject,
     translateText
-};
\ No newline at end of file
+};
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/main.test.js b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/main.test.js
index 7afa5fe..5dc6ee4 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/main.test.js
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/main.test.js
@@ -27,4 +27,4 @@ describe('module to test => function to test', () => {
 
 });
 
-// ... more test suites => describe
\ No newline at end of file
+// ... more test suites => describe
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/lib/setup.js b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/lib/setup.js
index 2ba2d59..9e7186c 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/lib/setup.js
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/lib/setup.js
@@ -723,4 +723,4 @@ if (typeof module !== undefined && module.parent) {
     module.exports.appName          = appName;
     module.exports.adapterName      = adapterName;
     module.exports.adapterStarted   = adapterStarted;
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testPackageFiles.js b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testPackageFiles.js
index d01db4c..7e8a1a4 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testPackageFiles.js
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testPackageFiles.js
@@ -83,4 +83,4 @@ describe('Test package.json and io-package.json', function() {
         }
         done();
     });
-});
\ No newline at end of file
+});
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testStartup.js b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testStartup.js
index a8cd7b2..72d4ae4 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testStartup.js
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/testStartup.js
@@ -136,4 +136,4 @@ describe('Test ' + adapterShortName + ' adapter', function() {
             done();
         });
     });
-});
\ No newline at end of file
+});
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/tsconfig.json b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/tsconfig.json
index c1db5fc..a2308c1 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/tsconfig.json
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/test/tsconfig.json
@@ -6,4 +6,4 @@
     "include": [
         "./**/*.js"
     ]
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/.travis.yml b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/.travis.yml
index 69b581d..3f99e77 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/.travis.yml
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/.travis.yml
@@ -29,4 +29,4 @@ before_script:
   - 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
 script:
   - npm run test:package
-  - npm run test:iobroker
\ No newline at end of file
+  - npm run test:iobroker
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/admin.d.ts b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/admin.d.ts
index cce2aad..13444d9 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/admin.d.ts
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/admin.d.ts
@@ -1,1 +1,1 @@
-declare let systemDictionary: Record<string, Record<string, string>>;
\ No newline at end of file
+declare let systemDictionary: Record<string, Record<string, string>>;
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/style.css b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/style.css
index 4612f7d..5d68855 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/style.css
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/style.css
@@ -7,4 +7,4 @@
 	overflow: initial;
 }
 
-/* Add your styles here */
\ No newline at end of file
+/* Add your styles here */
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/tsconfig.json b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/tsconfig.json
index 8f373d4..6dcddfa 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/tsconfig.json
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/admin/tsconfig.json
@@ -4,4 +4,4 @@
 		"./**/*.d.ts",
 		"./**/*.js"
 	]
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/lib/tools.ts b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/lib/tools.ts
index 63fce16..796a52a 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/lib/tools.ts
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/lib/tools.ts
@@ -40,4 +40,4 @@ export async function translateText(text: string, targetLang: string): Promise<s
 		throw new Error(`Could not translate to "${targetLang}": ${e}`);
 	}
 	return text;
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/main.test.ts b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/main.test.ts
index 226b80a..513a325 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/main.test.ts
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/src/main.test.ts
@@ -25,4 +25,4 @@ describe("module to test => function to test", () => {
 
 });
 
-// ... more test suites => describe
\ No newline at end of file
+// ... more test suites => describe
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/lib/setup.js b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/lib/setup.js
index 2dda6f5..9cf740c 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/lib/setup.js
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/lib/setup.js
@@ -723,4 +723,4 @@ if (typeof module !== undefined && module.parent) {
 	module.exports.appName          = appName;
 	module.exports.adapterName      = adapterName;
 	module.exports.adapterStarted   = adapterStarted;
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testPackageFiles.js b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testPackageFiles.js
index 2961aba..51cc9a8 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testPackageFiles.js
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testPackageFiles.js
@@ -83,4 +83,4 @@ describe("Test package.json and io-package.json", function() {
 		}
 		done();
 	});
-});
\ No newline at end of file
+});
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testStartup.js b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testStartup.js
index ac884a4..89e05d6 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testStartup.js
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/testStartup.js
@@ -136,4 +136,4 @@ describe("Test " + adapterShortName + " adapter", function() {
 			done();
 		});
 	});
-});
\ No newline at end of file
+});
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/tsconfig.json b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/tsconfig.json
index 0412f39..3ebc81b 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/tsconfig.json
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/test/tsconfig.json
@@ -6,4 +6,4 @@
 	"include": [
 		"./**/*.js"
 	]
-}
\ No newline at end of file
+}
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/tsconfig.build.json b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/tsconfig.build.json
index 1428762..e818694 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/tsconfig.build.json
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/tsconfig.build.json
@@ -13,4 +13,4 @@
 	"exclude": [
 		"src/**/*.test.ts"
 	]
-}
\ No newline at end of file
+}
diff --git a/test/baselines/vis_Widget/test/lib/setup.js b/test/baselines/vis_Widget/test/lib/setup.js
index 2dda6f5..9cf740c 100644
--- a/test/baselines/vis_Widget/test/lib/setup.js
+++ b/test/baselines/vis_Widget/test/lib/setup.js
@@ -723,4 +723,4 @@ if (typeof module !== undefined && module.parent) {
 	module.exports.appName          = appName;
 	module.exports.adapterName      = adapterName;
 	module.exports.adapterStarted   = adapterStarted;
-}
\ No newline at end of file
+}
diff --git a/test/baselines/vis_Widget/test/testPackageFiles.js b/test/baselines/vis_Widget/test/testPackageFiles.js
index 2961aba..51cc9a8 100644
--- a/test/baselines/vis_Widget/test/testPackageFiles.js
+++ b/test/baselines/vis_Widget/test/testPackageFiles.js
@@ -83,4 +83,4 @@ describe("Test package.json and io-package.json", function() {
 		}
 		done();
 	});
-});
\ No newline at end of file
+});
diff --git a/test/baselines/vis_Widget/test/testStartup.js b/test/baselines/vis_Widget/test/testStartup.js
index ac884a4..89e05d6 100644
--- a/test/baselines/vis_Widget/test/testStartup.js
+++ b/test/baselines/vis_Widget/test/testStartup.js
@@ -136,4 +136,4 @@ describe("Test " + adapterShortName + " adapter", function() {
 			done();
 		});
 	});
-});
\ No newline at end of file
+});