LoginSignup
7
7

More than 5 years have passed since last update.

Titanium + Alloy + TypeScript で、iOS & Android アプリを作る

Last updated at Posted at 2013-07-23

願望

Titanium(Alloy)を使って、iOSとAndroid両方のアプリを同時に作成したい。ついでに、TypeScriptを使いたい!

というわけで、いろいろやってみました。

準備

TypeScriptはインストールしておいてください。

$ npm install -g typescript

Macの場合は、npmの前にsudoが必要かもしれません。

alloy.jmk

参考:
http://k0suke.be/post/36585786781/alloy-with-coffeescript

TitaniumでAlloyプロジェクトを使っているときには、alloy.jmkというファイルを書いてやると、コンパイル前とコンパイル後にしたい処理を記述できるようです。

alloy.jmk
task("pre:compile", function(event,logger) {
    // コンパイル前処理
});

task("post:compile",function(event,logger){
    // コンパイル後処理
});

というふうに書けば、あとはTitaniumが自動で処理してくれます。

alloy.jmkでTypeScriptをコンパイル出来るように設定する

TypeScriptのソースを読むをいう記事の「まとめ」のところで書いたcompile関数をちょっと変更して、alloy.jmkファイルから呼び出すようにうまく書いてやればtypescriptで記述されたファイルを自動でjavascriptファイルに変換してビルド出来るでしょう。

Titaniumuで、"File" -> "New" -> "File"で、プロジェクトのルートに、"alloy.jmk"というファイルを作成します。
スクリーンショット 0025-07-23 12.57.37.png

その中の記述は以下を参考に。

gistにもアップしました。
https://gist.github.com/JunSuzukiJapan/6062233

alloy.jmk
function compile(TypeScript, logger, sourcefilename){
    var _this = this;

    var ByteOrderMark = {};
    ByteOrderMark[ByteOrderMark["None"] = 0] = "None";
    ByteOrderMark[ByteOrderMark["Utf8"] = 1] = "Utf8";
    ByteOrderMark[ByteOrderMark["Utf16BigEndian"] = 2] = "Utf16BigEndian";
    ByteOrderMark[ByteOrderMark["Utf16LittleEndian"] = 3] = "Utf16LittleEndian";

    var FileInformation = (function () {
        function FileInformation(contents, byteOrderMark) {
            this._contents = contents;
            this._byteOrderMark = byteOrderMark;
        }
        FileInformation.prototype.contents = function () {
            return this._contents;
        };

        FileInformation.prototype.byteOrderMark = function () {
            return this._byteOrderMark;
        };
        return FileInformation;
    })();

    var Environment = (function () {
        function getNodeEnvironment() {
            var _fs = require('fs');
            var _path = require('path');
            var _module = require('module');

            return {
                currentDirectory: function () {
                    return (process).cwd();
                },
                readFile: function (file) {
                    var buffer = _fs.readFileSync(file);
                    switch (buffer[0]) {
                        case 0xFE:
                            if (buffer[1] === 0xFF) {
                                var i = 0;
                                while ((i + 1) < buffer.length) {
                                    var temp = buffer[i];
                                    buffer[i] = buffer[i + 1];
                                    buffer[i + 1] = temp;
                                    i += 2;
                                }
                                return new FileInformation(buffer.toString("ucs2", 2), ByteOrderMark.Utf16BigEndian);
                            }
                            break;
                        case 0xFF:
                            if (buffer[1] === 0xFE) {
                                return new FileInformation(buffer.toString("ucs2", 2), ByteOrderMark.Utf16LittleEndian);
                            }
                            break;
                        case 0xEF:
                            if (buffer[1] === 0xBB) {
                                return new FileInformation(buffer.toString("utf8", 3), ByteOrderMark.Utf8);
                            }
                    }

                    return new FileInformation(buffer.toString("utf8", 0), ByteOrderMark.None);
                },
                writeFile: function (path, contents, writeByteOrderMark) {
                    function mkdirRecursiveSync(path) {
                        var stats = _fs.statSync(path);
                        if (stats.isFile()) {
                            throw "\"" + path + "\" exists but isn't a directory.";
                        } else if (stats.isDirectory()) {
                            return;
                        } else {
                            mkdirRecursiveSync(_path.dirname(path));
                            _fs.mkdirSync(path, 0775);
                        }
                    }
                    mkdirRecursiveSync(_path.dirname(path));

                    if (writeByteOrderMark) {
                        contents = '\uFEFF' + contents;
                    }
                    _fs.writeFileSync(path, contents, "utf8");
                },
                fileExists: function (path) {
                    return _fs.existsSync(path);
                },
                deleteFile: function (path) {
                    try  {
                        _fs.unlinkSync(path);
                    } catch (e) {
                    }
                },
                directoryExists: function (path) {
                    return _fs.existsSync(path) && _fs.statSync(path).isDirectory();
                },
                listFiles: function dir(path, spec, options) {
                    options = options || {};

                    function filesInFolder(folder) {
                        var paths = [];

                        var files = _fs.readdirSync(folder);
                        for (var i = 0; i < files.length; i++) {
                            var stat = _fs.statSync(folder + "\\" + files[i]);
                            if (options.recursive && stat.isDirectory()) {
                                paths = paths.concat(filesInFolder(folder + "\\" + files[i]));
                            } else if (stat.isFile() && (!spec || files[i].match(spec))) {
                                paths.push(folder + "\\" + files[i]);
                            }
                        }

                        return paths;
                    }

                    return filesInFolder(path);
                },
                arguments: process.argv.slice(2),
                standardOut: {
                    Write: function (str) {
                        process.stdout.write(str);
                    },
                    WriteLine: function (str) {
                        process.stdout.write(str + '\n');
                    },
                    Close: function () {
                    }
                }
            };
        }
        ;

        return getNodeEnvironment();
    })();

    var IOUtils;
    (function (IOUtils) {
        function createDirectoryStructure(ioHost, dirName) {
            if (ioHost.directoryExists(dirName)) {
                return;
            }

            var parentDirectory = ioHost.dirName(dirName);
            if (parentDirectory != "") {
                createDirectoryStructure(ioHost, parentDirectory);
            }
            ioHost.createDirectory(dirName);
        }

        function writeFileAndFolderStructure(ioHost, fileName, contents, writeByteOrderMark) {
            var path = ioHost.resolvePath(fileName);
            var dirName = ioHost.dirName(path);
            createDirectoryStructure(ioHost, dirName);
            return ioHost.writeFile(path, contents, writeByteOrderMark);
        }
        IOUtils.writeFileAndFolderStructure = writeFileAndFolderStructure;

        function throwIOError(message, error) {
            var errorMessage = message;
            if (error && error.message) {
                errorMessage += (" " + error.message);
            }
            throw new Error(errorMessage);
        }
        IOUtils.throwIOError = throwIOError;

        var BufferedTextWriter = (function () {
            function BufferedTextWriter(writer, capacity) {
                if (typeof capacity === "undefined") { capacity = 1024; }
                this.writer = writer;
                this.capacity = capacity;
                this.buffer = "";
            }
            BufferedTextWriter.prototype.Write = function (str) {
                this.buffer += str;
                if (this.buffer.length >= this.capacity) {
                    this.writer.Write(this.buffer);
                    this.buffer = "";
                }
            };
            BufferedTextWriter.prototype.WriteLine = function (str) {
                this.Write(str + '\r\n');
            };
            BufferedTextWriter.prototype.Close = function () {
                this.writer.Write(this.buffer);
                this.writer.Close();
                this.buffer = null;
            };
            return BufferedTextWriter;
        })();
        IOUtils.BufferedTextWriter = BufferedTextWriter;
    })(IOUtils || (IOUtils = {}));

    var IO = (function () {
        function getNodeIO() {
            var _fs = require('fs');
            var _path = require('path');
            var _module = require('module');

            return {
                readFile: function (file) {
                    return Environment.readFile(file);
                },
                writeFile: function (path, contents, writeByteOrderMark) {
                    Environment.writeFile(path, contents, writeByteOrderMark);
                },
                deleteFile: function (path) {
                    try  {
                        _fs.unlinkSync(path);
                    } catch (e) {
                        IOUtils.throwIOError("Couldn't delete file '" + path + "'.", e);
                    }
                },
                fileExists: function (path) {
                    return _fs.existsSync(path);
                },
                dir: function dir(path, spec, options) {
                    options = options || {};

                    function filesInFolder(folder) {
                        var paths = [];

                        try  {
                            var files = _fs.readdirSync(folder);
                            for (var i = 0; i < files.length; i++) {
                                var stat = _fs.statSync(folder + "/" + files[i]);
                                if (options.recursive && stat.isDirectory()) {
                                    paths = paths.concat(filesInFolder(folder + "/" + files[i]));
                                } else if (stat.isFile() && (!spec || files[i].match(spec))) {
                                    paths.push(folder + "/" + files[i]);
                                }
                            }
                        } catch (err) {
                        }

                        return paths;
                    }

                    return filesInFolder(path);
                },
                createDirectory: function (path) {
                    try  {
                        if (!this.directoryExists(path)) {
                            _fs.mkdirSync(path);
                        }
                    } catch (e) {
                        IOUtils.throwIOError("Couldn't create directory '" + path + "'.", e);
                    }
                },
                directoryExists: function (path) {
                    return _fs.existsSync(path) && _fs.statSync(path).isDirectory();
                },
                resolvePath: function (path) {
                    return _path.resolve(path);
                },
                dirName: function (path) {
                    return _path.dirname(path);
                },
                findFile: function (rootPath, partialFilePath) {
                    var path = rootPath + "/" + partialFilePath;

                    while (true) {
                        if (_fs.existsSync(path)) {
                            return { fileInformation: this.readFile(path), path: path };
                        } else {
                            var parentPath = _path.resolve(rootPath, "..");

                            if (rootPath === parentPath) {
                                return null;
                            } else {
                                rootPath = parentPath;
                                path = _path.resolve(rootPath, partialFilePath);
                            }
                        }
                    }
                },
                print: function (str) {
                    process.stdout.write(str);
                },
                printLine: function (str) {
                    process.stdout.write(str + '\n');
                },
                arguments: process.argv.slice(2),
                stderr: {
                    Write: function (str) {
                        process.stderr.write(str);
                    },
                    WriteLine: function (str) {
                        process.stderr.write(str + '\n');
                    },
                    Close: function () {
                    }
                },
                stdout: {
                    Write: function (str) {
                        process.stdout.write(str);
                    },
                    WriteLine: function (str) {
                        process.stdout.write(str + '\n');
                    },
                    Close: function () {
                    }
                },
                watchFile: function (fileName, callback) {
                    var firstRun = true;
                    var processingChange = false;

                    var fileChanged = function (curr, prev) {
                        if (!firstRun) {
                            if (curr.mtime < prev.mtime) {
                                return;
                            }

                            _fs.unwatchFile(fileName, fileChanged);
                            if (!processingChange) {
                                processingChange = true;
                                callback(fileName);
                                setTimeout(function () {
                                    processingChange = false;
                                }, 100);
                            }
                        }
                        firstRun = false;
                        _fs.watchFile(fileName, { persistent: true, interval: 500 }, fileChanged);
                    };

                    fileChanged();
                    return {
                        fileName: fileName,
                        close: function () {
                            _fs.unwatchFile(fileName, fileChanged);
                        }
                    };
                },
                run: function (source, fileName) {
                    require.main.fileName = fileName;
                    require.main.paths = _module._nodeModulePaths(_path.dirname(_fs.realpathSync(fileName)));
                    require.main._compile(source, fileName);
                },
                getExecutingFilePath: function () {
                    return process.mainModule.filename;
                },
                quit: process.exit
            };
        }
        ;

        return getNodeIO();
    })();

    var ErrorReporter = (function() {
        function ErrorReporter(ioHost, compilationEnvironment) {
            this.ioHost = ioHost;
            this.hasErrors = false;
            this.setCompilationEnvironment(compilationEnvironment);
        }

        ErrorReporter.prototype.addDiagnostic = function(diagnostic) {
            this.hasErrors = true;

            if (diagnostic.fileName()) {
                var soruceUnit = this.compilationEnvironment.getSourceUnit(diagnostic.fileName());
                if (!soruceUnit) {
                    soruceUnit = new TypeScript.SourceUnit(diagnostic.fileName(), this.ioHost.readFile(diagnostic.fileName()));
                }
                var lineMap = new TypeScript.LineMap(soruceUnit.getLineStartPositions(), soruceUnit.getLength());
                var lineCol = {
                    line : -1,
                    character : -1
                };
                lineMap.fillLineAndCharacterFromPosition(diagnostic.start(), lineCol);

                this.ioHost.stderr.Write(diagnostic.fileName() + "(" + (lineCol.line + 1) + "," + (lineCol.character + 1) + "): ");
            }

            this.ioHost.stderr.WriteLine(diagnostic.message());
        };

        ErrorReporter.prototype.setCompilationEnvironment = function(compilationEnvironment) {
            this.compilationEnvironment = compilationEnvironment;
        };

        ErrorReporter.prototype.reset = function() {
            this.hasErrors = false;
        };
        return ErrorReporter;
    })(); 

    this.ioHost = IO;
    this.hasResolveErrors = false;
    this.compilerVersion = "0.9.0.1";
    this.printedVersion = false;
    this.errorReporter = null;
    this.compilationSettings = new TypeScript.CompilationSettings();
    this.compilationEnvironment = new TypeScript.CompilationEnvironment(compilationSettings, ioHost);
    this.resolvedEnvironment = this.compilationEnvironment;
    this.errorReporter = new ErrorReporter(this.ioHost, this.compilationEnvironment);

    var compiler = new TypeScript.TypeScriptCompiler(logger, this.compilationSettings, null);

    //
    // ソースファイルの設定
    //
    var fileInformation = ioHost.readFile(sourcefilename);
    var code = new TypeScript.SourceUnit(sourcefilename, fileInformation);

    compiler.addSourceUnit(
        code.path,
        TypeScript.ScriptSnapshot.fromString(code.fileInformation.contents()),
        code.fileInformation.byteOrderMark(),
        0,
        false,
        code.referencedFiles);

    //
    // シンタックスエラーのチェック?
    //
    var syntacticDiagnostics = compiler.getSyntacticDiagnostics(code.path);
    compiler.reportDiagnostics(syntacticDiagnostics, this.errorReporter);

    var anySyntacticErrors = false;
    var anySemanticErrors = false;


    if (syntacticDiagnostics.length > 0) {
        anySyntacticErrors = true;  // シンタックスエラー有り
    }

    if (anySyntacticErrors) {
        return true; // エラーで終了
    }

    //
    // 型チェック
    //
    compiler.pullTypeCheck();

    //
    // コンパイル結果の書出し
    //

    // ファイル書出し用オブジェクト
    var emitterIOHost = {
        writeFile: function (fileName, contents, writeByteOrderMark) {
            return IOUtils.writeFileAndFolderStructure(_this.ioHost, fileName, contents, writeByteOrderMark);
        },
        directoryExists: this.ioHost.directoryExists,
        fileExists: this.ioHost.fileExists,
        resolvePath: this.ioHost.resolvePath
    };

    // よくわからない何か(ソースファイル名と出力ファイル名がどうしたこうした?)
    var mapInputToOutput = function (inputFile, outputFile) {
        _this.resolvedEnvironment.inputFileNameToOutputFileName.addOrUpdate(inputFile, outputFile);
    };

    // 発行(ファイルに書出し?)
    var emitDiagnostics = compiler.emitAll(emitterIOHost, mapInputToOutput);
    compiler.reportDiagnostics(emitDiagnostics, this.errorReporter);
    if (emitDiagnostics.length > 0) {
        return true; // エラーで終了
    }

    if (anySemanticErrors) {
        return true; // エラーで終了
    }

    // 発行(ファイルに書出し?) その2
    var emitDeclarationsDiagnostics = compiler.emitAllDeclarations();
    compiler.reportDiagnostics(emitDeclarationsDiagnostics, this.errorReporter);
    if (emitDeclarationsDiagnostics.length > 0) {
        return true; // エラーで終了
    }

    return false; // 無事コンパイル完了
}

// コンパイル前処理
task("pre:compile", function(event,logger) {
    var wrench = require("wrench"),
        fs = require("fs"),
        path = require("path");

    var code = [
        fs.readFileSync("/usr/local/lib/node_modules/typescript/bin/typescript.js"), // <-各自の環境に合わせて設定する。
        "module.exports = TypeScript;"
    ].join("");
    fs.writeFileSync(process.env.TMPDIR + "typescript.js", code);
    var TypeScript = require(process.env.TMPDIR + "typescript.js");
    fs.unlinkSync(process.env.TMPDIR + "typescript.js");

    logger.information = logger.info;
    logger.warning = logger.warn;
    logger.fatal = logger.info;
    logger.log = logger.info;

    event.alloyConfig.tsc = [];

    wrench.readdirSyncRecursive(event.dir.home).forEach(function(target){
        if (target.match(/\.ts$/) && ! target.match(/\.d\.ts$/)) { // .d.tsファイルはコンパイルしない。
            var filename = path.join(event.dir.home + "/" + target);
            compile(TypeScript, logger, filename);
            event.alloyConfig.tsc.push(target.replace(/.ts$/, ".js"));
        }
    });

 });

// コンパイル後処理
task("post:compile",function(event,logger){
  var fs = require("fs");

  event.alloyConfig.tsc.forEach(function(target){
    fs.unlinkSync(event.dir.home + "/" + target);
  });
});

あとは、TypeScriptでcontrollerなりmodelなりを書いて、Titaniumの"Run"コマンド、あるいは"Debug"コマンドを実行すればオッケーです。

TypeScriptのコンパイル時にエラーが表示されることがありますが、エラーがあってもJavaScriptに変換されていれば、そのまま処理が進みます。

wrenchまわりでひっかかる場合は、ターミナルで

npm install wrench

してやる必要があるかも…。

追記

2013/07/29
 ・*.d.tsファイルはコンパイルしないようにした(node.d.tsなどをコンパイルしたくないから)。
 ・意味解析部分を削除(エラーになることがあるので…)。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7