ひさびさの投稿
背景
Angular
で実装するときにつかうSystemJS
の設定ファイルだけcoffee-script
で記述していたのを、
4.x
がリリースされたのを機にTypescript
で書き直しました。
(coffee
のほうが楽だったからサボっていただけですね。。)
環境
$ node -v
v7.9.0
$ npm -v
4.6.1
$ tsc -v
Version 2.3.2
各種ファイル
package.json
package.json
{
"name": "heroes",
"version": "1.0.0",
"description": "This is Heroes App.",
"scripts": {
"p:client:w": "pug src -o assets --doctype html --pretty --watch",
"s:client:w": "node-sass src -o assets --outstyle expanded --watch",
"t:client:w": "tsc -p tsconfig-client.json --watch",
"t:server:w": "tsc -p tsconfig-server.json --watch",
"p:client": "pug src -o assets --doctype html",
"s:client": "node-sass src -o assets --outstyle compressed",
"t:client": "tsc -p tsconfig-client.json",
"t:server": "tsc -p tsconfig-server.json",
"build:cliend": "npm run p:client && npm run s:client && npm run t:client"
},
"author": "hoge hogeo <hoge.hogeo@example.com>",
"license": "MIT",
"dependencies": {
"@angular/animations": "^4.1.3",
"@angular/common": "^4.1.3",
"@angular/compiler": "^4.1.3",
"@angular/core": "^4.1.3",
"@angular/forms": "^4.1.3",
"@angular/http": "^4.1.3",
"@angular/platform-browser": "^4.1.3",
"@angular/platform-browser-dynamic": "^4.1.3",
"@angular/router": "^4.1.3",
"bootstrap": "^3.3.7",
"font-awesome": "^4.7.0",
"jquery": "^3.2.1",
"reflect-metadata": "^0.1.10",
"rxjs": "^5.4.0",
"systemjs": "^0.20.12",
"zone.js": "^0.8.11"
},
"devDependencies": {
"@types/bootstrap": "^3.3.33",
"@types/jquery": "^2.0.45"
}
}
tsconfig-client.json
tsconfig-client.json
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "system", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
"lib": ["es2015", "dom"], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "assets/scripts", /* Redirect output structure to the directory. */
"rootDir": "src/scripts", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
/// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
/// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */
},
"files": [
"src/scripts/bootstrap/my-app.ts",
"src/scripts/config/system.config.ts"
]
}
実装
system.config.ts
system.config.ts
declare var System: any;
let run = (bootstrap: string) => {
const APP_ROOT = "assets/scripts";
const BOOTSTRAP = "bootstrap/" + bootstrap + ".js";
let angularPrefix: string = "@angular";
let angularMaps: any = {};
let angularModules: string[] = [
"animations",
"animations/browser",
"common",
"compiler",
"core",
"forms",
"http",
"platform-browser",
"platform-browser-dynamic",
"platform-browser/animations",
"router",
];
let systemPaths: {[key: string]: string} = {
"npm:" : "node_modules/",
};
let systemMaps: {[key: string]: string} = {
[APP_ROOT] : APP_ROOT,
// 3rd Party Libraries
"rxjs" : "npm:rxjs",
"bootstrap" : "npm:bootstrap/dist/js/bootstrap.min.js",
"jquery" : "npm:jquery/dist/jquery.min.js",
};
let packages: {[key: string]: any} = {
[APP_ROOT] : { defaultExtension : "js" },
"rxjs" : { defaultExtension : "js" },
};
angularModules.forEach((module: string) => {
let _items: string[] = module.split("/");
Object.assign(angularMaps, {
[`${angularPrefix}/${module}`] : `${systemPaths["npm:"]}${angularPrefix}/${_items[0]}/bundles/${_items.join("-")}.umd.js`
});
});
Object.assign(systemMaps, angularMaps);
Object.assign(packages[APP_ROOT], { main : BOOTSTRAP });
System.config({
paths : systemPaths,
map : systemMaps,
packages : packages,
});
System.import(APP_ROOT).then(null, console.error.bind(console));
}
my-app.ts
src/scripts/bootstrap/my-app.ts
import {PRODUCTION} from "./env";
import {enableProdMode} from "@angular/core";
if (PRODUCTION) { enableProdMode(); }
import "jquery";
import "bootstrap";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {HeroAppModule} from "../app/hero-app.module";
platformBrowserDynamic().bootstrapModule(HeroAppModule);
読み込み
index.html
<!doctype html>
<html lang="ja">
<head>
<base href="/">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Heroes</title>
<link rel="shortcut icon" href="assets/images/favicon.ico" type="image/vnd.microsoft.icon">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css">
<body>
<my-app>Loading ...</my-app>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="assets/scripts/config/system.config.js"></script>
<script>run("my-app");</script>
あとがき
run()
をグローバル変数にしている理由は、
最初に読み込むHTMLをPHPから動的に生成し、
アプリケーションを切り替えられるようにするためです。
ですので
単一アプリケーションや、グローバル汚染が気になる場合は
let run = (bootstrap: string) => {
// ...
}
を
((bootstrap: string) => {
// ...
})("my-app");
としてあげればいいと思います。