вторник, 16 февраля 2016 г.

JavaScript Template Engine

function Template (textString, optionsObject) { // new Template("Total: <%= value %>", {delimiters: ["<%", '%>"]}).render({value: 10})
    if (!optionsObject) {optionsObject = {};}
    if (!optionsObject.hasOwnProperty("delimiters")) {
        optionsObject.delimiters = ["<%", "%>"];
    }
    var openDelimiter = optionsObject.delimiters[0] // <%
        , closeDelimiter = optionsObject.delimiters[1] // %>
        , stringInsideDelimiters = "([\\s\\S]+?)"          // some string
        , tags = {
              interpolate: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter  // <%= some string %>
            , escape:      openDelimiter + "-" + stringInsideDelimiters + closeDelimiter   // <%- some string %>
            , evaluate:    openDelimiter + stringInsideDelimiters + closeDelimiter             // <% some string %>
          }
        , matcherRegExp = RegExp([
              tags.interpolate
            , tags.escape
            , tags.evaluate // Important!!! Must be last!
          ].join("|") + "|$", "g") // /<%=([\s\S]+?)%>|<%-([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
       , escapeReqExp = new RegExp([
              "\\\\"       // single backslash
            , "'"            // single quote
            , "\\r"         // carriage return
            , "\\n"        // newline
            , "\\u2028" // line separator
            , "\\u2029" // paragraph separator
          ].join("|"), "g") // /\\|'|\r|\n|\u2028|\u2029/g
        , escapeCharacter = function (match) {
            var characters = {
                  "'":          "\\'"
                , "\\":         "\\\\"
                , "\r":         "\\r"
                , "\n":        "\\n"
                , "\u2028": "\\u2028"
                , "\u2029": "\\u2029"
            };
            return characters[match];
          }
        , escapeTags = function (string) {
            string = (string === null) ? "" : "" + string;
            var escapeMap = {
                      "&": "&amp;"
                    , "<": "&lt;"
                    , ">": "&gt;"
                    , '"':   "&quot;"
                    , "'":  "&#x27;"
                    , "`": "&#x60;"
                  }
                , escapeSymbols = function (match) {
                    return escapeMap[match];
                  }
                , getKeys = function (object) {
                    var keys = [];
                    for (var key in object) {
                        if (object.hasOwnProperty(key)) {keys.push(key);}
                    }
                    return keys;
                  }
                , pattern = "(?:" + getKeys(escapeMap).join("|") + ")" // (?:&|<|>|"|'|`)
                , testRegExp = RegExp(pattern)
                , replaceRegExp = RegExp(pattern, "g");
            return testRegExp.test(string) ? string.replace(replaceRegExp, escapeSymbols) : string;
          }
        , renderFunction
        , render;
    try {
        renderFunction = new Function(
                                                "dataObject"
                                              , "escapeTags"
                                              , "var temp"
                                             + "    , result = '';"
                                             + "with (dataObject || {}) {"
                                             + "    result += '" + (function(){
                                                    var resultString = ""
                                                        , index = 0;
                                                    textString.replace(matcherRegExp, function (match, interpolateCodeString, escapeCodeString, evaluateCodeString, offset) {
                                                        resultString += textString.slice(index, offset)
                                                                                                .replace(escapeReqExp, escapeCharacter);
                                                        index = offset + match.length;
                                                        if (interpolateCodeString) {
                                                            resultString += "' + ( ( temp = (" + interpolateCodeString + ") ) === null ? '' : temp) + '";
                                                        } else if (escapeCodeString) {
                                                            resultString += "' + ( ( temp = (" + escapeCodeString + ") ) === null ? '' : escapeTags(temp)) + '";
                                                        } else if (evaluateCodeString) {
                                                            resultString += "';" + evaluateCodeString + "result += '";
                                                        }
                                                        return match;
                                                    });
                                                    return resultString;
                                                })()
                                             + "';"
                                             + "}"
                                             + "return result;"
        );
    } catch (error) {
        throw error;
    }
    this.render = function (dataObject) {
        return renderFunction.call(null, dataObject, escapeTags);
    };
}

console.log(new Template("Total: <% if (true) { %>1<% } %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: <%= value %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: <%- value %>").render({value: '<div>1</div>'}));
console.log(new Template("Total: {{= value }}", {delimiters: ["{{", "}}"]}).render({value: '<div>1</div>'}));

среда, 10 февраля 2016 г.

TypeScript Helpers from tsc.js

function __extends(d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
function __decorate(decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __metadata(k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}
function __param(paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
}
function __awaiter(thisArg, _arguments, Promise, generator) {
    return new Promise(function (resolve, reject) {
        generator = generator.call(thisArg, _arguments);
        function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
        function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
        function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
        function step(verb, value) {
            var result = generator[verb](value);
            result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject);
        }
        step("next", void 0);
    });
}

вторник, 9 февраля 2016 г.

NPM не скачивает пакеты

Если NPM не может скачать пакеты в Node.js для версии выше 0.12, то необходимо в командной строке выполнить команду:

npm config set strict-ssl=false

После этого пакеты начнут скачиваться.

понедельник, 8 февраля 2016 г.

Как запустить Node.js без инсталяции

Если необходима портативная версия Node.js, то достаточно просто скачать файл
http://nodejs.org/dist/latest/node.exe
и поместить его в папку с вашими серверными JavaScript-файлами.
После этого в командной строке перейдите в папку вашего проекта и запустите ваш скрипт стандартным образом:

\path\to\saved\node.exe scriptname.js

При желании вы можете прописать ссылку на файл node.exe в системную переменную PATH, чтобы запускать ваши скрипты из любого места так:

node scriptname.js

Если вам нужен NPM, то скачайте его архив ZIP по ссылке

https://github.com/npm/npm

и распакуйте его в папку, в которую поместили файл node.exe.
Переименуйте папку с файлами NPM в "npm", убрав из названия номер версии версию, если он есть.
Создайте рядом с файлом node.exe папку "node_modules" и перенесите в неё папку "npm".
Скопируйте в папку с файлом node.exe файл npm.cmd из папки "node_modules/npm/bin/"

После этого NPM автоматически заработает из командной строки стандартным образом:

npm -h

Когда вам потребуется обновить Node.js, то просто замените файл node.exe на новую версию.

А для обновления NPM выполните команду:

npm update npm -g

или удалите старую папку node_modules и распакуйте в проект архив ZIP с новой версией NPM.

У меня стартовая структура проекта выглядит в итоге так:
C:\test\server.js - мой сценарий JavaScript
C:\test\node.exe - файл новой версии Node.js
C:\test\node_modules\npm - папка из архива ZIP с новой версией NPM
C:\test\npm.cmd - файл из архива ZIP с новой версией NPM

Если NPM не может скачать пакеты в Node.js для версии выше 0.12, то необходимо в командной строке выполнить команду:

npm config set strict-ssl=false

После этого пакеты начнут скачиваться.

вторник, 2 февраля 2016 г.

Настройка TypeScript + Grunt

1. В папку с проектом установить модули

npm install grunt
npm install grunt-contrib-watch
npm install grunt-typescript
npm install grunt-ts
npm install grunt-tslint
npm install grunt-jsbeautifier
npm install grunt-newer

2. Создать файл Gruntfile.js с кодом

module.exports = function (grunt) {

    var tsFiles = ['./js/**/*.ts'];

    grunt.initConfig({

        // Скомпилироать файлы TypeScript в JavaScript
        typescript: {
            base: {
                  src: tsFiles
                , dest: '' // '' - компиляция в разные файлы или './js/single.js' - компиляция в единый файл
                , options: {
                      module: 'amd' // 'amd' или 'commonjs'
                    , target: 'es3' // 'es6', 'es5' или 'es3' (по умолчанию)
                    , sourceMap: false // true, false - создавать ли файл '.map'
                    , module: 'amd' // генерация кода модулей в стиле: 'commonjs' (по умолчанию), 'amd', 'system' или 'umd'
                    , noImplicitAny: false // true // true, false - предупреждать ли о наличи в коде переменных и фунций с типом ':any'
                    , removeComments: false // true, false - удалять ли комментарии из итогового файла
                    , preserveConstEnums: false // true, false - не стирать определение const enum в генерируемом коде
                    , suppressImplicitAnyIndexErrors: true // true, false - подавлять ли сообщения об ошибках типа noImplicitAny для индексируемых объектов
                    , noEmitOnError: true // true (по умолчанию), false - не компилировать файл, если во время его проверки найдена ошибка
                    , declaration: false // true, false - создавать ли соотвествующий файл .d.ts
                    , nolib: true // true, false - не включать по умолчанию файл lib.d.ts в global declarations
                    , noResolve: false // true, false - не добавлять сслыку описание (///reference) или module import в список компилируемых файлов
                    , experimentalDecorators: true // true, false - включить экспермментальную поддержку декораторов из ES7
                    , emitDecoratorMetadata: true // true, false - сгенерировать метаданные для декораторов
                    , newLine: 'CRLF' // 'CRLF' (windows), 'LF' (unix) - тип переноса строк в компилируемых файлах
                    , inlineSourceMap: false // true, false - сгенерировать один файл source map вместо нескольких отдельных файлов
                    , inlineSources: false // true, false - сгенирировать инлайновый source map внутри единого файла (требует, чтобы был inlineSourceMap: true)
                    , noEmitHelpers: true // true, false - не генерировать вспомогательные функции в компилируемых файлах подобных '__extends'
                    , keepDirectoryHierarchy: true // true, false
                    /*
                    , references: [ // set auto reference libraries
                          'core' // lib.core.d.ts
                        , 'dom' // lib.dom.d.ts
                        , 'scriptHost' // lib.scriptHost.d.ts
                        , 'webworker' // lib.webworker.d.ts
                        //, 'path/to/reference/files/** /*.d.ts'
                    ]
                    , watch: { //{} или false
                          path: 'path/to/typescript/files' // или ['path/to/typescript/file1', 'path/to/typescript/file2']
                        , before: ['beforetask'] // ['clean']
                        , after: ['aftertask'] // ['minify']
                        , atBegin: true // true, false (по умолчания) - запустить ли задачи вместе с вотчером
                      }
                    */
                }
            }
         }

        // Другой компилятор TypeScript в JavaScript
        , ts: {
              default : {
                  src: ['js/test/module1.ts', 'js/test/module2.ts', 'js/test/module3.ts'] // ['js/**/*.ts']
                // , out: 'js/test/main.js'
                , options: {
                          declaration: false // true | false (default) - Generates a .d.ts definitions file for compiled TypeScript files
                        , emitDecoratorMetadata: false // true | false (default) - Emit metadata for type/parameter decorators.
                        , experimentalDecorators: false // true | false (default) - Enables experimental support for ES7 decorators
                        , inlineSourceMap: false // true | false (default) - Emit a single file that includes source maps instead of emitting a separate .js.map file.
                        , inlineSources: false // true | false (default) - Emit the TypeScript source alongside the sourcemaps within a single file. Requires inlineSourceMap to be set.
                        , isolatedModules: false // true | false (default) - Ensures that the output is safe to only emit single files by making cases that break single-file transpilation an error
                        , mapRoot: '' // '/maps' - Specifies the location where debugger should locate map files instead of generated locations.
                        , module: 'amd' // 'amd' (default) | 'commonjs' | 'system' | 'umd' | '' - Specify module style for code generation
                        , newLine: 'CRLF' // 'CRLF' | 'LF' | '' (default) -  Explicitly specify newline character (CRLF or LF); if omitted, uses OS default.
                        , noEmit: false // true | false (default) - Check, but do not emit JS, even in the absence of errors.
                        , noEmitHelpers: true // true | false (default) - Do not generate custom helper functions like __extends in compiled output.
                        , noImplicitAny: false // true | false (default) - Warn on expressions and declarations with an implied any type.
                        , noResolve: true // true | false (default) - Do not add triple-slash references or module import targets to the compilation context.
                        , out: '' // '' - компиляция в разные файлы или './js/single.js' - компиляция в единый файл - Concatenate and emit output to a single file.
                        , outDir: '' // 'dist' - Redirect output structure to the directory.
                        , preserveConstEnums: false // true | false (default) - Const enums will be kept as enums in the emitted JS.
                        , removeComments: false // true (default)| false - Configures if comments should be included in the output
                        , sourceMap: false // true (default) | false - Generates corresponding .map file
                        , sourceRoot: '' // '/dev' - Specifies the location where debugger should locate TypeScript files instead of source locations.
                        , suppressImplicitAnyIndexErrors:  false // true | false (default) - If set to true, TypeScript will allow access to properties of an object by string indexer when noImplicitAny is active, even if TypeScript doesn't know about them. This setting has no effect unless noImplicitAny is active.
                        , target: 'es3' // 'es3' | 'es5' (default) | 'es6' - Specify ECMAScript target version: 'es3', 'es5', or 'es6'
                  }
              }
          }

        // Проверить оформление кода в TypeScript-файлах
        , tslint: {
              options: {
                  configuration: grunt.file.readJSON('tslint.json')
              }
            , all: {
                  src: tsFiles
              }
        }

        // Форматирование кода JavaScript
        , 'jsbeautifier' : {
              files : ['js/**/*.js']
            , options : {
                js: {
                        braceStyle: 'collapse'
                      , breakChainedMethods: false
                      , e4x: false
                      , evalCode: false
                      , indentChar: ' '
                      , indentLevel: 0
                      , indentSize: 4
                      , indentWithTabs: false
                      , jslintHappy: true
                      , keepArrayIndentation: false
                      , keepFunctionIndentation: false
                      , maxPreserveNewlines: 10
                      , preserveNewlines: true
                      , spaceBeforeConditional: true
                      , spaceInParen: false
                      , unescapeStrings: false
                      , wrapLineLength: 0
                      , endWithNewline: false
                  }
              }
          }

        // Начать отслеживание изменений кода файлов
        , watch: {
              scripts: {
                  files: tsFiles
                , tasks: [
                    // 'newer:tslint'
                    //, 'typescript'
                      'ts'
                    , 'jsbeautifier'
                  ]
              }
          }

    });

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-ts');
    grunt.loadNpmTasks('grunt-tslint');
    grunt.loadNpmTasks('grunt-jsbeautifier');
    grunt.loadNpmTasks('grunt-newer');

    grunt.registerTask('default', [
        //  'newer:tslint'
        //, 'typescript'
          'ts'
        , 'jsbeautifier'
        , 'watch'
    ]);

};

3. Создать файл rungrunt.js с кодом

require('grunt').cli();

4. Создать файл tsling.json с кодом

{
  "rules": {
      "ban": false
    , "class-name": true
    , "comment-format": [true, "check-space"]
    , "curly": true
    , "eofline": false
    , "forin": true
    , "indent": [true, "spaces"]
    , "jsdoc-format": true
    , "label-position": true
    , "label-undefined": true
    , "max-line-length": [true, 500]
    , "member-access": true
    , "member-ordering": [
          true
        , "public-before-private"
        , "static-before-instance"
        , "variables-before-functions"
      ]
    , "no-any": true
    , "no-arg": true
    , "no-bitwise": true
    , "no-console": [
          true
        , "debug"
        , "info"
        , "time"
        , "timeEnd"
        , "trace"
      ]
    , "no-consecutive-blank-lines": true
    , "no-construct": true
    , "no-constructor-vars": true
    , "no-debugger": false
    , "no-duplicate-key": true
    , "no-duplicate-variable": true
    , "no-empty": true
    , "no-eval": true
    , "no-internal-module": true
    , "no-string-literal": false
    , "no-trailing-comma": true
    , "no-trailing-whitespace": true
    , "no-unreachable": true
    , "no-unused-expression": true
    , "no-unused-variable": true
    , "no-use-before-declare": true
    , "one-line": [
          true
        , "check-open-brace"
        , "check-catch"
        , "check-else"
        , "check-whitespace"
      ]
    , "quotemark": [true, "single"]
    , "radix": true
    , "semicolon": true
    , "switch-default": true
    , "triple-equals": [true, "allow-null-check"]
    , "typedef": [
          true
        , "call-signature"
        , "parameter"
        , "property"
        , "variable-declaration"
        , "member-variable-declarations"
      ]
    , "typedef-whitespace": [
          true
        , {
              "call-signature": "nospace"
            , "index-signature": "nospace"
            , "parameter": "nospace"
            , "property-declaration": "nospace"
            , "variable-declaration": "nospace"
        }
      ]
    , "variable-name": false
    , "whitespace": [
          true
        , "check-branch"
        , "check-decl"
        , "check-module"
        , "check-operator"
        , "check-separator"
      ]
  }
}

5. Поместить исходные TypeScript-файлы в папку js

js/module-1.ts
js/module-2.ts

6. Скомпилировать TypeScript-файлы в JavaScript-файлы командой

node rungrunt.js

Настройка TypeScript + Gulp

1. В папку с проектом установить модули

npm install gulp
npm install gulp-typescript
npm install gulp-tslint

2. Создать файл Gulpfile.js с кодом

var gulp = require('gulp')
    , typescript = require('gulp-typescript')
    , typescriptLint = require('gulp-tslint');
   
// Lint all typescript files
gulp.task('typescript-lint', function () {
    return gulp.src(['src/**/*.ts']).pipe(typescriptLint({configuration: 'tslint.json'}))
                                                .pipe(typescriptLint.report('prose'));
});
   
// Build all typescript files
gulp.task('typescript-build', function () {
    var typescriptResult = gulp.src(['src/**/*.ts']).pipe(typescript(typescript.createProject('tsconfig.json')));
    return typescriptResult.js.pipe(gulp.dest('build'));
});

// Watch all typescript files for changes and rebuild everything
gulp.task('typescript-watch', function () {
    gulp.watch(['src/**/*.ts'], [
          'typescript-lint'
        , 'typescript-build'
    ]);
});

// Default gulp task
gulp.task('default', [
      'typescript-lint'
    , 'typescript-build'
    , 'typescript-watch'
]);

3. Создать файл rungulp.js с кодом

// Execute gulp task
var command = 'node ./node_modules/gulp/bin/gulp.js default'
    , process = require('child_process').exec(command);
process.stdout.on('data', function(data) {console.log(data);});
process.stderr.on('data', function(data) {console.log(data);});
process.on('close', function(code) {if (code === 0) {console.log('Done');} else {console.log('Exit code: ' + code);}});

4. Создать файл tsconfig.json с кодом

{
      "compilerOptions": {
              "declaration": false
            , "emitDecoratorMetadata": false
            , "experimentalDecorators": false
            , "inlineSourceMap": false
            , "inlineSources": false
            , "isolatedModules": false
            , "mapRoot": ""
            , "module": "amd"
            , "newLine": "CRLF"
            , "noEmit": false
            , "noEmitHelpers": true
            , "noImplicitAny": true
            , "noResolve": true
            , "outFile": "final.js"
            , "preserveConstEnums": false
            , "removeComments": false
            , "sourceMap": false
            , "sourceRoot": ""
            , "suppressImplicitAnyIndexErrors": false
            , "target": "es3"
      }
}

5. Создать файл tsling.json с кодом

{
  "rules": {
      "ban": false
    , "class-name": true
    , "comment-format": [true, "check-space"]
    , "curly": true
    , "eofline": false
    , "forin": true
    , "indent": [true, "spaces"]
    , "jsdoc-format": true
    , "label-position": true
    , "label-undefined": true
    , "max-line-length": [true, 500]
    , "member-access": true
    , "member-ordering": [
          true
        , "public-before-private"
        , "static-before-instance"
        , "variables-before-functions"
      ]
    , "no-any": true
    , "no-arg": true
    , "no-bitwise": true
    , "no-console": [
          true
        , "debug"
        , "info"
        , "time"
        , "timeEnd"
        , "trace"
      ]
    , "no-consecutive-blank-lines": true
    , "no-construct": true
    , "no-constructor-vars": true
    , "no-debugger": false
    , "no-duplicate-key": true
    , "no-duplicate-variable": true
    , "no-empty": true
    , "no-eval": true
    , "no-internal-module": true
    , "no-string-literal": false
    , "no-trailing-comma": true
    , "no-trailing-whitespace": true
    , "no-unreachable": true
    , "no-unused-expression": true
    , "no-unused-variable": true
    , "no-use-before-declare": true
    , "one-line": [
          true
        , "check-open-brace"
        , "check-catch"
        , "check-else"
        , "check-whitespace"
      ]
    , "quotemark": [true, "single"]
    , "radix": true
    , "semicolon": true
    , "switch-default": true
    , "triple-equals": [true, "allow-null-check"]
    , "typedef": [
          true
        , "call-signature"
        , "parameter"
        , "property"
        , "variable-declaration"
        , "member-variable-declarations"
      ]
    , "typedef-whitespace": [
          true
        , {
              "call-signature": "nospace"
            , "index-signature": "nospace"
            , "parameter": "nospace"
            , "property-declaration": "nospace"
            , "variable-declaration": "nospace"
        }
      ]
    , "variable-name": false
    , "whitespace": [
          true
        , "check-branch"
        , "check-decl"
        , "check-module"
        , "check-operator"
        , "check-separator"
      ]
  }
}

6. Поместить исходные TypeScript-файлы в папку src

src/module-1.ts
src/module-2.ts

7. Скомпилировать TypeScript-файлы в JavaScript-файлы командой

node rungulp.js

понедельник, 25 января 2016 г.

Как запустить Gulp локально

Устанавливаем локально Gulp в папку с проектом:
cd C:\Work\My\Folder
npm install gulp

Создаем файл gulpfile.js с кодом

  var gulp = require('gulp');

  gulp.task('build', function () {
    console.log('Building...');
  });

  gulp.task('default', function () {
    console.log('Hello, world!');
  });

4 способа запустить задания из файла gulpfile.js.

1) Запустить из файла rungulp.js

Создать файл rungulp.js:

var exec = require('child_process').exec;
exec('node ./node_modules/gulp/bin/gulp.js build', function(error, stdout, stderr) {
    console.log('stdout: ', stdout);
    console.log('stderr: ', stderr);
    if (error !== null) {
        console.log('exec error: ', error);
    }
});

// или
/*
var child = exec('node ./node_modules/gulp/bin/gulp.js build');
child.stdout.on('data', function(data) {console.log('stdout: ' + data);});
child.stderr.on('data', function(data) {console.log('stderr: ' + data);});
child.on('close', function(code) {console.log('closing code: ' + code);});

*/

Запустить его из командной строки:
node rungulp.js

2) Запустить из командной строки файл gulp
C:\Work\My\Folder\node_modules\.bin\gulp

3) Создать файл package.json с кодом

  {
    // ...
    "scripts": {
      "gulp": "gulp"
    }
    // ...
  }

Выполнить в командной строке команду
npm run gulp

4) Установить Gulp глобально
npm install -g gulp

Выполнить в командной строке команду
gulp