chore(lint): lint ts as well as js (#1823)

This commit is contained in:
Filipe Silva 2016-08-25 18:06:54 +01:00 committed by Hans
parent b5e86c9fa9
commit d17bc71a0b
40 changed files with 376 additions and 251 deletions

View File

@ -17,7 +17,12 @@ module.exports = Command.extend({
aliases: ['b'], aliases: ['b'],
availableOptions: [ availableOptions: [
{ name: 'target', type: String, default: 'development', aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }] }, {
name: 'target',
type: String,
default: 'development',
aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }]
},
{ name: 'environment', type: String, default: '', aliases: ['e'] }, { name: 'environment', type: String, default: '', aliases: ['e'] },
{ name: 'output-path', type: 'Path', default: 'dist/', aliases: ['o'] }, { name: 'output-path', type: 'Path', default: 'dist/', aliases: ['o'] },
{ name: 'watch', type: Boolean, default: false, aliases: ['w'] }, { name: 'watch', type: Boolean, default: false, aliases: ['w'] },
@ -26,18 +31,18 @@ module.exports = Command.extend({
], ],
run: function (commandOptions: BuildOptions) { run: function (commandOptions: BuildOptions) {
if (commandOptions.environment === ''){ if (commandOptions.environment === '') {
if (commandOptions.target === 'development') { if (commandOptions.target === 'development') {
commandOptions.environment = 'dev'; commandOptions.environment = 'dev';
} }
if (commandOptions.target === 'production') { if (commandOptions.target === 'production') {
commandOptions.environment = 'prod'; commandOptions.environment = 'prod';
} }
} }
var project = this.project; const project = this.project;
var ui = this.ui; const ui = this.ui;
var buildTask = commandOptions.watch ? const buildTask = commandOptions.watch ?
new WebpackBuildWatch({ new WebpackBuildWatch({
cliProject: project, cliProject: project,
ui: ui, ui: ui,

View File

@ -10,10 +10,10 @@ const DocCommand = Command.extend({
'<keyword>' '<keyword>'
], ],
run: function(commandOptions, rawArgs:Array<string>) { run: function(commandOptions, rawArgs: Array<string>) {
var keyword = rawArgs[0]; const keyword = rawArgs[0];
var docTask = new DocTask({ const docTask = new DocTask({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project project: this.project
@ -23,4 +23,4 @@ const DocCommand = Command.extend({
} }
}); });
module.exports = DocCommand; module.exports = DocCommand;

View File

@ -9,7 +9,7 @@ module.exports = Command.extend({
run: function () { run: function () {
this.project.ngConfig = this.project.ngConfig || CliConfig.fromProject(); this.project.ngConfig = this.project.ngConfig || CliConfig.fromProject();
var e2eTask = new E2ETask({ const e2eTask = new E2ETask({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project project: this.project

View File

@ -2,9 +2,9 @@ import * as EmberGenerateCommand from 'ember-cli/lib/commands/generate';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as SilentError from 'silent-error'; import * as SilentError from 'silent-error';
var chalk = require('chalk');
import * as Blueprint from 'ember-cli/lib/models/blueprint'; import * as Blueprint from 'ember-cli/lib/models/blueprint';
var EOL = require('os').EOL; const chalk = require('chalk');
const EOL = require('os').EOL;
const GenerateCommand = EmberGenerateCommand.extend({ const GenerateCommand = EmberGenerateCommand.extend({
name: 'generate', name: 'generate',
@ -21,16 +21,16 @@ const GenerateCommand = EmberGenerateCommand.extend({
!fs.existsSync(path.join(__dirname, '..', 'blueprints', rawArgs[0]))) { !fs.existsSync(path.join(__dirname, '..', 'blueprints', rawArgs[0]))) {
SilentError.debugOrThrow('angular-cli/commands/generate', `Invalid blueprint: ${rawArgs[0]}`); SilentError.debugOrThrow('angular-cli/commands/generate', `Invalid blueprint: ${rawArgs[0]}`);
} }
// Override default help to hide ember blueprints // Override default help to hide ember blueprints
EmberGenerateCommand.prototype.printDetailedHelp = function (options) { EmberGenerateCommand.prototype.printDetailedHelp = function (options) {
var blueprintList = fs.readdirSync(path.join(__dirname, '..', 'blueprints')); const blueprintList = fs.readdirSync(path.join(__dirname, '..', 'blueprints'));
var blueprints = blueprintList const blueprints = blueprintList
.filter(bp => bp.indexOf('-test') === -1) .filter(bp => bp.indexOf('-test') === -1)
.filter(bp => bp !== 'ng2') .filter(bp => bp !== 'ng2')
.map(bp => Blueprint.load(path.join(__dirname, '..', 'blueprints', bp))); .map(bp => Blueprint.load(path.join(__dirname, '..', 'blueprints', bp)));
var output = ''; let output = '';
blueprints blueprints
.forEach(function (bp) { .forEach(function (bp) {
output += bp.printBasicHelp(false) + EOL; output += bp.printBasicHelp(false) + EOL;
@ -38,7 +38,7 @@ const GenerateCommand = EmberGenerateCommand.extend({
this.ui.writeLine(chalk.cyan(' Available blueprints')); this.ui.writeLine(chalk.cyan(' Available blueprints'));
this.ui.writeLine(output); this.ui.writeLine(output);
}; };
return EmberGenerateCommand.prototype.beforeRun.apply(this, arguments); return EmberGenerateCommand.prototype.beforeRun.apply(this, arguments);
} }
}); });

View File

@ -9,6 +9,7 @@ import * as path from 'path';
import * as WebpackBuild from '../tasks/build-webpack'; import * as WebpackBuild from '../tasks/build-webpack';
import * as CreateGithubRepo from '../tasks/create-github-repo'; import * as CreateGithubRepo from '../tasks/create-github-repo';
import { CliConfig } from '../models/config'; import { CliConfig } from '../models/config';
import { oneLine } from 'common-tags';
const fsReadFile = Promise.denodeify(fs.readFile); const fsReadFile = Promise.denodeify(fs.readFile);
const fsWriteFile = Promise.denodeify(fs.writeFile); const fsWriteFile = Promise.denodeify(fs.writeFile);
@ -18,7 +19,10 @@ const fsCopy = Promise.denodeify(fse.copy);
module.exports = Command.extend({ module.exports = Command.extend({
name: 'github-pages:deploy', name: 'github-pages:deploy',
aliases: ['gh-pages:deploy'], aliases: ['gh-pages:deploy'],
description: 'Build the test app for production, commit it into a git branch, setup GitHub repo and push to it', description: oneLine`
Build the test app for production, commit it into a git branch,
setup GitHub repo and push to it
`,
works: 'insideProject', works: 'insideProject',
availableOptions: [ availableOptions: [
@ -60,13 +64,13 @@ module.exports = Command.extend({
}], }],
run: function(options, rawArgs) { run: function(options, rawArgs) {
var ui = this.ui; const ui = this.ui;
var root = this.project.root; const root = this.project.root;
var execOptions = { const execOptions = {
cwd: root cwd: root
}; };
if (options.environment === ''){ if (options.environment === '') {
if (options.target === 'development') { if (options.target === 'development') {
options.environment = 'dev'; options.environment = 'dev';
} }
@ -75,7 +79,7 @@ module.exports = Command.extend({
} }
} }
var projectName = this.project.pkg.name; const projectName = this.project.pkg.name;
const outDir = CliConfig.fromProject().config.apps[0].outDir; const outDir = CliConfig.fromProject().config.apps[0].outDir;
@ -86,7 +90,7 @@ module.exports = Command.extend({
// declared here so that tests can stub exec // declared here so that tests can stub exec
const execPromise = Promise.denodeify(exec); const execPromise = Promise.denodeify(exec);
var buildTask = new WebpackBuild({ const buildTask = new WebpackBuild({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
cliProject: this.project, cliProject: this.project,
@ -95,19 +99,19 @@ module.exports = Command.extend({
outputPath: outDir outputPath: outDir
}); });
var buildOptions = { const buildOptions = {
target: options.target, target: options.target,
environment: options.environment, environment: options.environment,
outputPath: outDir outputPath: outDir
}; };
var createGithubRepoTask = new CreateGithubRepo({ const createGithubRepoTask = new CreateGithubRepo({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project project: this.project
}); });
var createGithubRepoOptions = { const createGithubRepoOptions = {
projectName, projectName,
ghUsername: options.ghUsername, ghUsername: options.ghUsername,
ghToken: options.ghToken ghToken: options.ghToken
@ -137,7 +141,7 @@ module.exports = Command.extend({
} }
function build() { function build() {
if (options.skipBuild) return Promise.resolve(); if (options.skipBuild) { return Promise.resolve(); }
return buildTask.run(buildOptions); return buildTask.run(buildOptions);
} }
@ -165,7 +169,7 @@ module.exports = Command.extend({
function checkoutGhPages() { function checkoutGhPages() {
return execPromise(`git checkout ${ghPagesBranch}`) return execPromise(`git checkout ${ghPagesBranch}`)
.catch(createGhPagesBranch) .catch(createGhPagesBranch);
} }
function createGhPagesBranch() { function createGhPagesBranch() {
@ -179,16 +183,16 @@ module.exports = Command.extend({
function copyFiles() { function copyFiles() {
return fsReadDir(outDir) return fsReadDir(outDir)
.then((files) => Promise.all(files.map((file) => { .then((files) => Promise.all(files.map((file) => {
if (file === '.gitignore'){ if (file === '.gitignore') {
// don't overwrite the .gitignore file // don't overwrite the .gitignore file
return Promise.resolve(); return Promise.resolve();
} }
return fsCopy(path.join(outDir, file), path.join('.', file)) return fsCopy(path.join(outDir, file), path.join('.', file));
}))); })));
} }
function updateBaseHref() { function updateBaseHref() {
if (options.userPage) return Promise.resolve(); if (options.userPage) { return Promise.resolve(); }
let indexHtml = path.join(root, 'index.html'); let indexHtml = path.join(root, 'index.html');
return fsReadFile(indexHtml, 'utf8') return fsReadFile(indexHtml, 'utf8')
.then((data) => data.replace(/<base href="\/">/g, `<base href="/${projectName}/">`)) .then((data) => data.replace(/<base href="\/">/g, `<base href="/${projectName}/">`))
@ -215,7 +219,8 @@ module.exports = Command.extend({
function printProjectUrl() { function printProjectUrl() {
return execPromise('git remote -v') return execPromise('git remote -v')
.then((stdout) => { .then((stdout) => {
let userName = stdout.match(/origin\s+(?:https:\/\/|git@)github\.com(?:\:|\/)([^\/]+)/m)[1].toLowerCase(); let match = stdout.match(/origin\s+(?:https:\/\/|git@)github\.com(?:\:|\/)([^\/]+)/m);
let userName = match[1].toLowerCase();
let url = `https://${userName}.github.io/${options.userPage ? '' : (projectName + '/')}`; let url = `https://${userName}.github.io/${options.userPage ? '' : (projectName + '/')}`;
ui.writeLine(chalk.green(`Deployed! Visit ${url}`)); ui.writeLine(chalk.green(`Deployed! Visit ${url}`));
ui.writeLine('Github pages might take a few minutes to show the deployed site.'); ui.writeLine('Github pages might take a few minutes to show the deployed site.');
@ -225,7 +230,8 @@ module.exports = Command.extend({
function failGracefully(error) { function failGracefully(error) {
if (error && (/git clean/.test(error.message) || /Permission denied/.test(error.message))) { if (error && (/git clean/.test(error.message) || /Permission denied/.test(error.message))) {
ui.writeLine(error.message); ui.writeLine(error.message);
let msg = 'There was a permissions error during git file operations, please close any open project files/folders and try again.'; let msg = 'There was a permissions error during git file operations, ' +
'please close any open project files/folders and try again.';
msg += `\nYou might also need to return to the ${initialBranch} branch manually.`; msg += `\nYou might also need to return to the ${initialBranch} branch manually.`;
return Promise.reject(new SilentError(msg)); return Promise.reject(new SilentError(msg));
} else { } else {

View File

@ -6,7 +6,7 @@ module.exports = Command.extend({
description: 'Lints code in existing project', description: 'Lints code in existing project',
works: 'insideProject', works: 'insideProject',
run: function () { run: function () {
var lintTask = new LintTask({ const lintTask = new LintTask({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project project: this.project

View File

@ -3,7 +3,6 @@ import * as Command from 'ember-cli/lib/models/command';
import * as Promise from 'ember-cli/lib/ext/promise'; import * as Promise from 'ember-cli/lib/ext/promise';
import * as SilentError from 'silent-error'; import * as SilentError from 'silent-error';
import * as PortFinder from 'portfinder'; import * as PortFinder from 'portfinder';
import * as EOL from 'os';
import * as ServeWebpackTask from '../tasks/serve-webpack.ts'; import * as ServeWebpackTask from '../tasks/serve-webpack.ts';
PortFinder.basePort = 49152; PortFinder.basePort = 49152;
@ -36,15 +35,46 @@ module.exports = Command.extend({
availableOptions: [ availableOptions: [
{ name: 'port', type: Number, default: defaultPort, aliases: ['p'] }, { name: 'port', type: Number, default: defaultPort, aliases: ['p'] },
{ name: 'host', type: String, default: 'localhost', aliases: ['H'], description: 'Listens on all interfaces by default' }, {
name: 'host',
type: String,
default: 'localhost',
aliases: ['H'],
description: 'Listens on all interfaces by default'
},
{ name: 'proxy-config', type: 'Path', aliases: ['pc'] }, { name: 'proxy-config', type: 'Path', aliases: ['pc'] },
{ name: 'watcher', type: String, default: 'events', aliases: ['w'] }, { name: 'watcher', type: String, default: 'events', aliases: ['w'] },
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] }, { name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
{ name: 'live-reload-host', type: String, aliases: ['lrh'], description: 'Defaults to host' }, {
{ name: 'live-reload-base-url', type: String, aliases: ['lrbu'], description: 'Defaults to baseURL' }, name: 'live-reload-host',
{ name: 'live-reload-port', type: Number, aliases: ['lrp'], description: '(Defaults to port number within [49152...65535])' }, type: String,
{ name: 'live-reload-live-css', type: Boolean, default: true, description: 'Whether to live reload CSS (default true)' }, aliases: ['lrh'],
{ name: 'target', type: String, default: 'development', aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }] }, description: 'Defaults to host'
},
{
name: 'live-reload-base-url',
type: String,
aliases: ['lrbu'],
description: 'Defaults to baseURL'
},
{
name: 'live-reload-port',
type: Number,
aliases: ['lrp'],
description: '(Defaults to port number within [49152...65535])'
},
{
name: 'live-reload-live-css',
type: Boolean,
default: true,
description: 'Whether to live reload CSS (default true)'
},
{
name: 'target',
type: String,
default: 'development',
aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }]
},
{ name: 'environment', type: String, default: '', aliases: ['e'] }, { name: 'environment', type: String, default: '', aliases: ['e'] },
{ name: 'ssl', type: Boolean, default: false }, { name: 'ssl', type: Boolean, default: false },
{ name: 'ssl-key', type: String, default: 'ssl/server.key' }, { name: 'ssl-key', type: String, default: 'ssl/server.key' },
@ -52,7 +82,7 @@ module.exports = Command.extend({
], ],
run: function(commandOptions: ServeTaskOptions) { run: function(commandOptions: ServeTaskOptions) {
if (commandOptions.environment === ''){ if (commandOptions.environment === '') {
if (commandOptions.target === 'development') { if (commandOptions.target === 'development') {
commandOptions.environment = 'dev'; commandOptions.environment = 'dev';
} }
@ -65,20 +95,12 @@ module.exports = Command.extend({
return this._checkExpressPort(commandOptions) return this._checkExpressPort(commandOptions)
.then(this._autoFindLiveReloadPort.bind(this)) .then(this._autoFindLiveReloadPort.bind(this))
.then((commandOptions: ServeTaskOptions) => { .then((opts: ServeTaskOptions) => {
commandOptions = assign({}, commandOptions, { commandOptions = assign({}, opts, {
baseURL: this.project.config(commandOptions.target).baseURL || '/' baseURL: this.project.config(commandOptions.target).baseURL || '/'
}); });
if (commandOptions.proxy) { const serve = new ServeWebpackTask({
if (!commandOptions.proxy.match(/^(http:|https:)/)) {
var message = 'You need to include a protocol with the proxy URL.' + EOL + 'Try --proxy http://' + commandOptions.proxy;
return Promise.reject(new SilentError(message));
}
}
var serve = new ServeWebpackTask({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project, project: this.project,
@ -93,7 +115,7 @@ module.exports = Command.extend({
.then((foundPort: number) => { .then((foundPort: number) => {
if (commandOptions.port !== foundPort && commandOptions.port !== 0) { if (commandOptions.port !== foundPort && commandOptions.port !== 0) {
var message = 'Port ' + commandOptions.port + ' is already in use.'; const message = 'Port ' + commandOptions.port + ' is already in use.';
return Promise.reject(new SilentError(message)); return Promise.reject(new SilentError(message));
} }

View File

@ -16,7 +16,7 @@ module.exports = TestCommand.extend({
run: function (commandOptions) { run: function (commandOptions) {
this.project.ngConfig = this.project.ngConfig || CliConfig.fromProject(); this.project.ngConfig = this.project.ngConfig || CliConfig.fromProject();
var testTask = new TestTask({ const testTask = new TestTask({
ui: this.ui, ui: this.ui,
analytics: this.analytics, analytics: this.analytics,
project: this.project project: this.project

View File

@ -14,18 +14,18 @@ const VersionCommand = Command.extend({
}], }],
run: function (options) { run: function (options) {
var versions = process.versions; const versions = process.versions;
var pkg = require(path.resolve(__dirname, '..', '..', '..', 'package.json')); const pkg = require(path.resolve(__dirname, '..', '..', '..', 'package.json'));
versions['os'] = process.platform + ' ' + process.arch; versions['os'] = process.platform + ' ' + process.arch;
var alwaysPrint = ['node', 'os']; const alwaysPrint = ['node', 'os'];
var ngCliVersion = pkg.version; let ngCliVersion = pkg.version;
if (!__dirname.match(/node_modules/)) { if (!__dirname.match(/node_modules/)) {
var gitBranch = '??'; let gitBranch = '??';
try { try {
var gitRefName = '' + child_process.execSync('git symbolic-ref HEAD', {cwd: __dirname}); const gitRefName = '' + child_process.execSync('git symbolic-ref HEAD', {cwd: __dirname});
gitBranch = path.basename(gitRefName.replace('\n', '')); gitBranch = path.basename(gitRefName.replace('\n', ''));
} catch (e) { } catch (e) {
} }
@ -35,7 +35,7 @@ const VersionCommand = Command.extend({
this.printVersion('angular-cli', ngCliVersion); this.printVersion('angular-cli', ngCliVersion);
for (var module in versions) { for (const module in versions) {
if (options.verbose || alwaysPrint.indexOf(module) > -1) { if (options.verbose || alwaysPrint.indexOf(module) > -1) {
this.printVersion(module, versions[module]); this.printVersion(module, versions[module]);
} }

View File

@ -3,7 +3,7 @@ interface IWebpackDevServerConfigurationOptions {
hot?: boolean; hot?: boolean;
historyApiFallback?: boolean; historyApiFallback?: boolean;
compress?: boolean; compress?: boolean;
proxy?: {[key: string] : string}; proxy?: {[key: string]: string};
staticOptions?: any; staticOptions?: any;
quiet?: boolean; quiet?: boolean;
noInfo?: boolean; noInfo?: boolean;
@ -14,8 +14,8 @@ interface IWebpackDevServerConfigurationOptions {
poll?: number; poll?: number;
}; };
publicPath?: string; publicPath?: string;
headers?: { [key:string]: string }; headers?: { [key: string]: string };
stats?: { [key:string]: boolean }; stats?: { [key: string]: boolean };
inline: boolean; inline: boolean;
} }

View File

@ -1,12 +1,10 @@
import {CliConfig as CliConfigBase} from './config/config'; import {CliConfig as CliConfigBase} from './config/config';
import {CliConfig as ConfigInterface} from '../../../lib/config/schema'; import {CliConfig as ConfigInterface} from '../../../lib/config/schema';
import { oneLine } from 'common-tags';
import * as chalk from 'chalk'; import * as chalk from 'chalk';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
const schemaPath = path.resolve(process.env.CLI_ROOT, 'lib/config/schema.json');
const schema = require(schemaPath);
export const CLI_CONFIG_FILE_NAME = 'angular-cli.json'; export const CLI_CONFIG_FILE_NAME = 'angular-cli.json';
@ -51,11 +49,11 @@ export class CliConfig extends CliConfigBase<ConfigInterface> {
const cliConfig = CliConfigBase.fromConfigPath(CliConfig._configFilePath(), [globalConfigPath]); const cliConfig = CliConfigBase.fromConfigPath(CliConfig._configFilePath(), [globalConfigPath]);
if (cliConfig.alias('apps.0.root', 'defaults.sourceDir') if (cliConfig.alias('apps.0.root', 'defaults.sourceDir')
+ cliConfig.alias('apps.0.prefix', 'defaults.prefix')) { + cliConfig.alias('apps.0.prefix', 'defaults.prefix')) {
console.error(chalk.yellow( console.error(chalk.yellow(oneLine`
'The "defaults.prefix" and "defaults.sourceDir" properties of angular-cli.json\n' The "defaults.prefix" and "defaults.sourceDir" properties of angular-cli.json
+ 'are deprecated in favor of "apps[0].root" and "apps[0].prefix".\n' are deprecated in favor of "apps[0].root" and "apps[0].prefix".\n
+ 'Please update in order to avoid errors in future versions of angular-cli.' Please update in order to avoid errors in future versions of angular-cli.
)); `));
} }
return cliConfig as CliConfig; return cliConfig as CliConfig;

View File

@ -17,7 +17,7 @@ export class InvalidConfigError extends Error {
export class CliConfig<Config> { export class CliConfig<Config> {
private _config: SchemaClass<Config>; private _config: SchemaClass<Config>;
private constructor(private _configPath: string, constructor(private _configPath: string,
schema: Object, schema: Object,
configJson: Config, configJson: Config,
fallbacks: Config[] = []) { fallbacks: Config[] = []) {
@ -29,7 +29,7 @@ export class CliConfig<Config> {
save(path: string = this._configPath) { save(path: string = this._configPath) {
return fs.writeFileSync(path, this.serialize(), 'utf-8'); return fs.writeFileSync(path, this.serialize(), 'utf-8');
} }
serialize(mimetype: string = 'application/json'): string { serialize(mimetype = 'application/json'): string {
return this._config.$$serialize(mimetype); return this._config.$$serialize(mimetype);
} }
@ -73,7 +73,7 @@ export class CliConfig<Config> {
try { try {
content = JSON.parse(configContent); content = JSON.parse(configContent);
schema = JSON.parse(schemaContent); schema = JSON.parse(schemaContent);
others = otherContents.map(content => JSON.parse(content)); others = otherContents.map(content => JSON.parse(otherContent));
} catch (err) { } catch (err) {
throw new InvalidConfigError(err); throw new InvalidConfigError(err);
} }

View File

@ -3,13 +3,9 @@ import * as glob from 'glob';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {Observable} from 'rxjs/Observable';
import {getSource, findNodes, getContentOfKeyLiteral} from '../utilities/ast-utils'; import {getSource, findNodes, getContentOfKeyLiteral} from '../utilities/ast-utils';
const loadChildrenRegex = /(\{[^{}]+?(loadChildren|['"]loadChildren['"])\s*:\s*)('[^']+'|"[^"]+")/gm;
interface Array<T> { interface Array<T> {
flatMap: <R>(mapFn: (item: T) => Array<R>) => Array<R>; flatMap: <R>(mapFn: (item: T) => Array<R>) => Array<R>;
} }
@ -37,15 +33,15 @@ export function findLoadChildren(tsFilePath: string): string[] {
// key is an expression, can't do anything. // key is an expression, can't do anything.
return false; return false;
} }
return key == 'loadChildren' return key == 'loadChildren';
}) })
// Remove initializers that are not files. // Remove initializers that are not files.
.filter((node: ts.PropertyAssignment) => { .filter((node: ts.PropertyAssignment) => {
return node.initializer.kind === ts.SyntaxKind.StringLiteral; return node.initializer.kind === ts.SyntaxKind.StringLiteral;
}) })
// Get the full text of the initiliazer. // Get the full text of the initializer.
.map((node: ts.PropertyAssignment) => { .map((node: ts.PropertyAssignment) => {
return eval(node.initializer.getText(source)); return eval(node.initializer.getText(source)); // tslint:disable-line
}) })
.flatMap((value: string) => unique[value] ? undefined : unique[value] = value) .flatMap((value: string) => unique[value] ? undefined : unique[value] = value)
.map((moduleName: string) => moduleName.split('#')[0]); .map((moduleName: string) => moduleName.split('#')[0]);

View File

@ -108,7 +108,7 @@ class SchemaClassBase<T> implements SchemaClass<T> {
$$set(path: string, value: any) { $$set(path: string, value: any) {
const node = _getSchemaNodeForPath(this[kSchemaNode], path); const node = _getSchemaNodeForPath(this[kSchemaNode], path);
if (node) { if (node) {
node.set(value) node.set(value);
} else { } else {
// This might be inside an object that can have additionalProperties, so // This might be inside an object that can have additionalProperties, so
// a TreeNode would not exist. // a TreeNode would not exist.
@ -137,7 +137,7 @@ class SchemaClassBase<T> implements SchemaClass<T> {
return node ? node.defined : false; return node ? node.defined : false;
} }
$$delete(path: string){ $$delete(path: string) {
const node = _getSchemaNodeForPath(this[kSchemaNode], path); const node = _getSchemaNodeForPath(this[kSchemaNode], path);
if (node) { if (node) {
node.destroy(); node.destroy();
@ -145,7 +145,7 @@ class SchemaClassBase<T> implements SchemaClass<T> {
} }
/** Serialize into a string. */ /** Serialize into a string. */
$$serialize(mimetype: string = 'application/json', ...options: any[]): string { $$serialize(mimetype = 'application/json', ...options: any[]): string {
let str = ''; let str = '';
const serializer = Serializer.fromMimetype(mimetype, (s) => str += s, ...options); const serializer = Serializer.fromMimetype(mimetype, (s) => str += s, ...options);

View File

@ -75,7 +75,7 @@ export abstract class SchemaTreeNode<T> {
get parent<ParentType>(): SchemaTreeNode<ParentType> { return this._parent; } get parent<ParentType>(): SchemaTreeNode<ParentType> { return this._parent; }
get children<ChildType>(): { [key: string]: SchemaTreeNode<ChildType>} { return null; } get children<ChildType>(): { [key: string]: SchemaTreeNode<ChildType>} { return null; }
abstract get() : T; abstract get(): T;
set(v: T) { set(v: T) {
if (!this.readOnly) { if (!this.readOnly) {
throw new MissingImplementationError(); throw new MissingImplementationError();
@ -105,7 +105,7 @@ export abstract class SchemaTreeNode<T> {
/** Base Class used for Non-Leaves TreeNode. Meaning they can have children. */ /** Base Class used for Non-Leaves TreeNode. Meaning they can have children. */
abstract class NonLeafSchemaTreeNode<T> extends SchemaTreeNode<T> { abstract class NonLeafSchemaTreeNode<T> extends SchemaTreeNode<T> {
dispose() { dispose() {
for (const key of Object.keys(this.children) { for (const key of Object.keys(this.children)) {
this.children[key].dispose(); this.children[key].dispose();
} }
super.dispose(); super.dispose();
@ -132,7 +132,7 @@ abstract class NonLeafSchemaTreeNode<T> extends SchemaTreeNode<T> {
const type = schema['type']; const type = schema['type'];
let Klass: TreeNodeConstructor = null; let Klass: TreeNodeConstructor = null;
switch(type) { switch (type) {
case 'object': Klass = ObjectSchemaTreeNode; break; case 'object': Klass = ObjectSchemaTreeNode; break;
case 'array': Klass = ArraySchemaTreeNode; break; case 'array': Klass = ArraySchemaTreeNode; break;
case 'string': Klass = StringSchemaTreeNode; break; case 'string': Klass = StringSchemaTreeNode; break;

View File

@ -60,9 +60,6 @@ class JsonSerializer implements Serializer {
private _top() { private _top() {
return this._state[this._state.length - 1] || {}; return this._state[this._state.length - 1] || {};
} }
private _topIsArray() {
return this._top().type == 'array';
}
private _indent(): string { private _indent(): string {
if (this._indentDelta == 0) { if (this._indentDelta == 0) {

View File

@ -4,7 +4,7 @@ import * as HtmlWebpackPlugin from 'html-webpack-plugin';
import * as webpack from 'webpack'; import * as webpack from 'webpack';
import * as atl from 'awesome-typescript-loader'; import * as atl from 'awesome-typescript-loader';
import {findLazyModules} from './find-lazy-modules'; import { findLazyModules } from './find-lazy-modules';
export function getWebpackCommonConfig(projectRoot: string, environment: string, appConfig: any) { export function getWebpackCommonConfig(projectRoot: string, environment: string, appConfig: any) {
@ -14,13 +14,13 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,
const scripts = appConfig.scripts.map(script => path.resolve(appRoot, script)); const scripts = appConfig.scripts.map(script => path.resolve(appRoot, script));
const lazyModules = findLazyModules(appRoot); const lazyModules = findLazyModules(appRoot);
let entry = { let entry = {
main: [appMain] main: [appMain]
}; };
// Only add styles/scripts if there's actually entries there // Only add styles/scripts if there's actually entries there
if (appConfig.styles.length > 0) entry.styles = styles; if (appConfig.styles.length > 0) { entry['styles'] = styles; }
if (appConfig.scripts.length > 0) entry.scripts = scripts; if (appConfig.scripts.length > 0) { entry['scripts'] = scripts; }
return { return {
devtool: 'source-map', devtool: 'source-map',
@ -62,16 +62,42 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,
}, },
// in main, load css as raw text // in main, load css as raw text
       { exclude: styles, test: /\.css$/, loaders: ['raw-loader', 'postcss-loader'] },        {
       { exclude: styles, test: /\.styl$/, loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] }, exclude: styles,
       { exclude: styles, test: /\.less$/, loaders: ['raw-loader', 'postcss-loader', 'less-loader'] }, test: /\.css/,
       { exclude: styles, test: /\.scss$|\.sass$/, loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] }, loaders: ['raw-loader', 'postcss-loader']
}, {
exclude: styles,
test: /\.styl$/,
loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] },
       {
exclude: styles,
test: /\.less$/,
loaders: ['raw-loader', 'postcss-loader', 'less-loader']
}, {
exclude: styles,
test: /\.scss$|\.sass$/,
loaders: ['raw-loader', 'postcss-loader', 'sass-loader']
},
// outside of main, load it via style-loader // outside of main, load it via style-loader
       { include: styles, test: /\.css$/, loaders: ['style-loader', 'css-loader', 'postcss-loader'] },        {
       { include: styles, test: /\.styl$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader'] }, include: styles,
       { include: styles, test: /\.less$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'] }, test: /\.css$/,
       { include: styles, test: /\.scss$|\.sass$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] }, loaders: ['style-loader', 'css-loader', 'postcss-loader']
}, {
include: styles,
test: /\.styl$/,
loaders: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader']
}, {
include: styles,
test: /\.less$/,
loaders: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}, {
include: styles,
test: /\.scss$|\.sass$/,
loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
},
// load global scripts using script-loader // load global scripts using script-loader
{ include: scripts, test: /\.js$/, loader: 'script-loader' }, { include: scripts, test: /\.js$/, loader: 'script-loader' },
@ -97,7 +123,7 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,
// Since it takes a RegExp as first parameter, we need to escape the path. // Since it takes a RegExp as first parameter, we need to escape the path.
// See https://webpack.github.io/docs/list-of-plugins.html#normalmodulereplacementplugin // See https://webpack.github.io/docs/list-of-plugins.html#normalmodulereplacementplugin
new RegExp(path.resolve(appRoot, appConfig.environments['source']) new RegExp(path.resolve(appRoot, appConfig.environments['source'])
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")), .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')),
path.resolve(appRoot, appConfig.environments[environment]) path.resolve(appRoot, appConfig.environments[environment])
), ),
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
@ -124,5 +150,5 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,
clearImmediate: false, clearImmediate: false,
setImmediate: false setImmediate: false
} }
} };
} }

View File

@ -1,4 +1,4 @@
const path = require('path') const path = require('path');
export const getWebpackDevConfigPartial = function(projectRoot: string, appConfig: any) { export const getWebpackDevConfigPartial = function(projectRoot: string, appConfig: any) {
return { return {
@ -24,4 +24,4 @@ export const getWebpackDevConfigPartial = function(projectRoot: string, appConfi
setImmediate: false setImmediate: false
} }
}; };
} };

View File

@ -1,4 +1,3 @@
import * as webpack from 'webpack';
import * as path from 'path'; import * as path from 'path';
import * as OfflinePlugin from 'offline-plugin'; import * as OfflinePlugin from 'offline-plugin';
import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import * as CopyWebpackPlugin from 'copy-webpack-plugin';
@ -10,8 +9,13 @@ export const getWebpackMobileConfigPartial = function (projectRoot: string, appC
return { return {
plugins: [ plugins: [
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{from: path.resolve(projectRoot, appConfig.root, 'icons'), to: path.resolve(projectRoot, appConfig.outDir, 'icons')}, {
{from: path.resolve(projectRoot, appConfig.root, 'manifest.webapp'), to: path.resolve(projectRoot, appConfig.outDir)} from: path.resolve(projectRoot, appConfig.root, 'icons'),
to: path.resolve(projectRoot, appConfig.outDir, 'icons')
}, {
from: path.resolve(projectRoot, appConfig.root, 'manifest.webapp'),
to: path.resolve(projectRoot, appConfig.outDir)
}
]), ]),
new PrerenderWebpackPlugin({ new PrerenderWebpackPlugin({
templatePath: 'index.html', templatePath: 'index.html',
@ -19,7 +23,7 @@ export const getWebpackMobileConfigPartial = function (projectRoot: string, appC
appPath: path.resolve(projectRoot, appConfig.root) appPath: path.resolve(projectRoot, appConfig.root)
}) })
] ]
} };
}; };
export const getWebpackMobileProdConfigPartial = function (projectRoot: string, appConfig: any) { export const getWebpackMobileProdConfigPartial = function (projectRoot: string, appConfig: any) {
@ -30,5 +34,5 @@ export const getWebpackMobileProdConfigPartial = function (projectRoot: string,
plugins: [ plugins: [
new OfflinePlugin() new OfflinePlugin()
] ]
} };
}; };

View File

@ -1,5 +1,4 @@
import * as path from 'path'; import * as path from 'path';
import * as webpackMerge from 'webpack-merge'; // used to merge webpack configs
import * as WebpackMd5Hash from 'webpack-md5-hash'; import * as WebpackMd5Hash from 'webpack-md5-hash';
import * as CompressionPlugin from 'compression-webpack-plugin'; import * as CompressionPlugin from 'compression-webpack-plugin';
import * as webpack from 'webpack'; import * as webpack from 'webpack';
@ -17,12 +16,11 @@ export const getWebpackProdConfigPartial = function(projectRoot: string, appConf
plugins: [ plugins: [
new WebpackMd5Hash(), new WebpackMd5Hash(),
new webpack.optimize.DedupePlugin(), new webpack.optimize.DedupePlugin(),
// ~107kb
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
beautify: false, //prod beautify: false,
mangle: { screw_ie8 : true, keep_fnames: true }, //prod mangle: { screw_ie8 : true, keep_fnames: true },
compress: { screw_ie8: true }, //prod compress: { screw_ie8: true },
comments: false //prod comments: false
}), }),
new CompressionPlugin({ new CompressionPlugin({
asset: '[path].gz[query]', asset: '[path].gz[query]',
@ -57,5 +55,5 @@ export const getWebpackProdConfigPartial = function(projectRoot: string, appConf
clearImmediate: false, clearImmediate: false,
setImmediate: false setImmediate: false
} }
} };
}; };

View File

@ -2,15 +2,15 @@ import * as path from 'path';
export const ngAppResolve = (resolvePath: string): string => { export const ngAppResolve = (resolvePath: string): string => {
return path.resolve(process.cwd(), resolvePath); return path.resolve(process.cwd(), resolvePath);
} };
export const webpackOutputOptions: WebpackProgressPluginOutputOptions = { export const webpackOutputOptions = {
colors: true, colors: true,
chunks: true, chunks: true,
modules: false, modules: false,
reasons: false, reasons: false,
chunkModules: false chunkModules: false
} };
export const webpackDevServerOutputOptions = { export const webpackDevServerOutputOptions = {
assets: true, assets: true,
@ -20,4 +20,4 @@ export const webpackDevServerOutputOptions = {
timings: true, timings: true,
chunks: false, chunks: false,
chunkModules: false chunkModules: false
} };

View File

@ -12,27 +12,31 @@ export class NgCliWebpackConfig {
// TODO: When webpack2 types are finished lets replace all these any types // TODO: When webpack2 types are finished lets replace all these any types
// so this is more maintainable in the future for devs // so this is more maintainable in the future for devs
public config: any; public config: any;
private webpackDevConfigPartial: any; private devConfigPartial: any;
private webpackProdConfigPartial: any; private prodConfigPartial: any;
private webpackBaseConfig: any; private baseConfig: any;
private webpackMobileConfigPartial: any;
private webpackMobileProdConfigPartial: any;
constructor(public ngCliProject: any, public target: string, public environment: string, outputDir?: string) { constructor(
public ngCliProject: any,
public target: string,
public environment: string,
outputDir?: string
) {
const config: CliConfig = CliConfig.fromProject(); const config: CliConfig = CliConfig.fromProject();
const appConfig = config.config.apps[0]; const appConfig = config.config.apps[0];
appConfig.outDir = outputDir || appConfig.outDir; appConfig.outDir = outputDir || appConfig.outDir;
this.webpackBaseConfig = getWebpackCommonConfig(this.ngCliProject.root, environment, appConfig); this.baseConfig = getWebpackCommonConfig(this.ngCliProject.root, environment, appConfig);
this.webpackDevConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, appConfig); this.devConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, appConfig);
this.webpackProdConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, appConfig); this.prodConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, appConfig);
if (appConfig.mobile){ if (appConfig.mobile) {
this.webpackMobileConfigPartial = getWebpackMobileConfigPartial(this.ngCliProject.root, appConfig); let mobileConfigPartial = getWebpackMobileConfigPartial(this.ngCliProject.root, appConfig);
this.webpackMobileProdConfigPartial = getWebpackMobileProdConfigPartial(this.ngCliProject.root, appConfig); let mobileProdConfigPartial = getWebpackMobileProdConfigPartial(this.ngCliProject.root,
this.webpackBaseConfig = webpackMerge(this.webpackBaseConfig, this.webpackMobileConfigPartial); appConfig);
this.webpackProdConfigPartial = webpackMerge(this.webpackProdConfigPartial, this.webpackMobileProdConfigPartial); this.baseConfig = webpackMerge(this.baseConfig, mobileConfigPartial);
this.prodConfigPartial = webpackMerge(this.prodConfigPartial, mobileProdConfigPartial);
} }
this.generateConfig(); this.generateConfig();
@ -40,15 +44,14 @@ export class NgCliWebpackConfig {
generateConfig(): void { generateConfig(): void {
switch (this.target) { switch (this.target) {
case "development": case 'development':
this.config = webpackMerge(this.webpackBaseConfig, this.webpackDevConfigPartial); this.config = webpackMerge(this.baseConfig, this.devConfigPartial);
break; break;
case "production": case 'production':
this.config = webpackMerge(this.webpackBaseConfig, this.webpackProdConfigPartial); this.config = webpackMerge(this.baseConfig, this.prodConfigPartial);
break; break;
default: default:
throw new Error("Invalid build target. Only 'development' and 'production' are available."); throw new Error("Invalid build target. Only 'development' and 'production' are available.");
break;
} }
} }
} }

View File

@ -16,7 +16,12 @@ module.exports = Task.extend({
rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath)); rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath));
const config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment, runTaskOptions.outputPath).config; const config = new NgCliWebpackConfig(
project,
runTaskOptions.target,
runTaskOptions.environment,
runTaskOptions.outputPath
).config;
const webpackCompiler = webpack(config); const webpackCompiler = webpack(config);
webpackCompiler.apply(new ProgressPlugin({ webpackCompiler.apply(new ProgressPlugin({
@ -28,15 +33,15 @@ module.exports = Task.extend({
if (err) { if (err) {
lastHash = null; lastHash = null;
console.error(err.stack || err); console.error(err.stack || err);
if(err.details) console.error(err.details); if (err.details) { console.error(err.details); }
reject(err.details); reject(err.details);
} }
if(stats.hash !== lastHash) { if (stats.hash !== lastHash) {
lastHash = stats.hash; lastHash = stats.hash;
process.stdout.write(stats.toString(webpackOutputOptions) + "\n"); process.stdout.write(stats.toString(webpackOutputOptions) + '\n');
} }
}) });
}) });
} }
}); });

View File

@ -13,11 +13,16 @@ module.exports = Task.extend({
// Options: String outputPath // Options: String outputPath
run: function(runTaskOptions: ServeTaskOptions) { run: function(runTaskOptions: ServeTaskOptions) {
var project = this.cliProject; const project = this.cliProject;
rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath)); rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath));
var config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment, runTaskOptions.outputPath).config; const config = new NgCliWebpackConfig(
project,
runTaskOptions.target,
runTaskOptions.environment,
runTaskOptions.outputPath
).config;
const webpackCompiler = webpack(config); const webpackCompiler = webpack(config);
const ProgressPlugin = require('webpack/lib/ProgressPlugin'); const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@ -32,16 +37,16 @@ module.exports = Task.extend({
// TODO: Make conditional if using --watch // TODO: Make conditional if using --watch
webpackCompiler.purgeInputFileSystem(); webpackCompiler.purgeInputFileSystem();
if(err) { if (err) {
lastHash = null; lastHash = null;
console.error(err.stack || err); console.error(err.stack || err);
if(err.details) console.error(err.details); if (err.details) { console.error(err.details); }
reject(err.details); reject(err.details);
} }
if(stats.hash !== lastHash) { if (stats.hash !== lastHash) {
lastHash = stats.hash; lastHash = stats.hash;
process.stdout.write(stats.toString(webpackOutputOptions) + "\n"); process.stdout.write(stats.toString(webpackOutputOptions) + '\n');
} }
resolve(); resolve();
}); });

View File

@ -3,10 +3,11 @@ import * as Task from 'ember-cli/lib/models/task';
import * as SilentError from 'silent-error'; import * as SilentError from 'silent-error';
import { exec } from 'child_process'; import { exec } from 'child_process';
import * as https from 'https'; import * as https from 'https';
import { oneLine } from 'common-tags';
module.exports = Task.extend({ module.exports = Task.extend({
run: function(commandOptions) { run: function(commandOptions) {
var ui = this.ui; const ui = this.ui;
let promise; let promise;
// declared here so that tests can stub exec // declared here so that tests can stub exec
@ -18,16 +19,24 @@ module.exports = Task.extend({
ghUsername: commandOptions.ghUsername ghUsername: commandOptions.ghUsername
}); });
} else { } else {
ui.writeLine("\nIn order to deploy this project via GitHub Pages, we must first create a repository for it."); ui.writeLine();
ui.writeLine("It's safer to use a token than to use a password, so you will need to create one.\n"); ui.writeLine(oneLine`
ui.writeLine("Go to the following page and click 'Generate new token'."); In order to deploy this project via GitHub Pages, we must first create a repository for it.
ui.writeLine("https://github.com/settings/tokens\n"); `);
ui.writeLine("Choose 'public_repo' as scope and then click 'Generate token'.\n"); ui.writeLine(oneLine`
It\'s safer to use a token than to use a password so you will need to create one
`);
ui.writeLine('Go to the following page and click "Generate new token".');
ui.writeLine('https://github.com/settings/tokens\n');
ui.writeLine('Choose "public_repo" as scope and then click "Generate token".\n');
promise = ui.prompt([ promise = ui.prompt([
{ {
name: 'ghToken', name: 'ghToken',
type: 'input', type: 'input',
message: 'Please enter GitHub token you just created (used only once to create the repo):', message: oneLine`
Please enter GitHub token you just created
(used only once to create the repo):
`,
validate: function(token) { validate: function(token) {
return /.+/.test(token); return /.+/.test(token);
} }
@ -44,11 +53,11 @@ module.exports = Task.extend({
return promise return promise
.then((answers) => { .then((answers) => {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var postData = JSON.stringify({ const postData = JSON.stringify({
'name': commandOptions.projectName 'name': commandOptions.projectName
}); });
var req = https.request({ const req = https.request({
hostname: 'api.github.com', hostname: 'api.github.com',
port: 443, port: 443,
path: '/user/repos', path: '/user/repos',
@ -63,9 +72,14 @@ module.exports = Task.extend({
req.on('response', function(response) { req.on('response', function(response) {
if (response.statusCode === 201) { if (response.statusCode === 201) {
resolve(execPromise(`git remote add origin git@github.com:${answers.ghUsername}/${commandOptions.projectName}.git`)) resolve(execPromise(oneLine`
git remote add origin
git@github.com:${answers.ghUsername}/${commandOptions.projectName}.git
`));
} else { } else {
reject(new SilentError(`Failed to create GitHub repo. Error: ${response.statusCode} ${response.statusMessage}`)); reject(new SilentError(oneLine`
Failed to create GitHub repo. Error: ${response.statusCode} ${response.statusMessage}
`));
} }
}); });

View File

@ -3,9 +3,9 @@ import * as opn from 'opn';
const DocTask = Task.extend({ const DocTask = Task.extend({
run: function(keyword: string) { run: function(keyword: string) {
var searchUrl = `https://angular.io/docs/ts/latest/api/#!?apiFilter=${keyword}`; const searchUrl = `https://angular.io/docs/ts/latest/api/#!?apiFilter=${keyword}`;
return opn(searchUrl, { wait: false }); return opn(searchUrl, { wait: false });
} }
}); });
module.exports = DocTask; module.exports = DocTask;

View File

@ -5,21 +5,22 @@ import {exec} from 'child_process';
module.exports = Task.extend({ module.exports = Task.extend({
run: function () { run: function () {
var ui = this.ui; const ui = this.ui;
var exitCode = 0; let exitCode = 0;
return new Promise((resolve) => { return new Promise((resolve) => {
exec(`npm run e2e -- ${this.project.ngConfig.config.e2e.protractor.config}`, (err, stdout, stderr) => { exec(`npm run e2e -- ${this.project.ngConfig.config.e2e.protractor.config}`,
ui.writeLine(stdout); (err, stdout, stderr) => {
if (err) { ui.writeLine(stdout);
ui.writeLine(stderr); if (err) {
ui.writeLine(chalk.red('Some end-to-end tests failed, see above.')); ui.writeLine(stderr);
exitCode = err.code; ui.writeLine(chalk.red('Some end-to-end tests failed, see above.'));
} else { exitCode = err.code;
ui.writeLine(chalk.green('All end-to-end tests pass.')); } else {
} ui.writeLine(chalk.green('All end-to-end tests pass.'));
resolve(exitCode); }
}); resolve(exitCode);
});
}); });
} }
}); });

View File

@ -5,7 +5,7 @@ import {exec} from 'child_process';
module.exports = Task.extend({ module.exports = Task.extend({
run: function() { run: function() {
var ui = this.ui; const ui = this.ui;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
exec('npm link angular-cli', (err) => { exec('npm link angular-cli', (err) => {

View File

@ -5,7 +5,7 @@ import {exec} from 'child_process';
module.exports = Task.extend({ module.exports = Task.extend({
run: function () { run: function () {
var ui = this.ui; const ui = this.ui;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
exec('npm run lint', (err, stdout) => { exec('npm run lint', (err, stdout) => {

View File

@ -10,18 +10,24 @@ import { webpackDevServerOutputOptions } from '../models/';
import { NgCliWebpackConfig } from '../models/webpack-config'; import { NgCliWebpackConfig } from '../models/webpack-config';
import { ServeTaskOptions } from '../commands/serve'; import { ServeTaskOptions } from '../commands/serve';
import { CliConfig } from '../models/config'; import { CliConfig } from '../models/config';
import { oneLine } from 'common-tags';
module.exports = Task.extend({ module.exports = Task.extend({
run: function(commandOptions: ServeTaskOptions) { run: function(commandOptions: ServeTaskOptions) {
const ui = this.ui;
let lastHash = null;
let webpackCompiler: any; let webpackCompiler: any;
var config = new NgCliWebpackConfig(this.project, commandOptions.target, commandOptions.environment).config; let config = new NgCliWebpackConfig(
this.project, commandOptions.target,
commandOptions.environment
).config;
// This allows for live reload of page when changes are made to repo. // This allows for live reload of page when changes are made to repo.
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode // https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
config.entry.main.unshift(`webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/`); config.entry.main.unshift(
`webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/`
);
webpackCompiler = webpack(config); webpackCompiler = webpack(config);
webpackCompiler.apply(new ProgressPlugin({ webpackCompiler.apply(new ProgressPlugin({
@ -35,39 +41,39 @@ module.exports = Task.extend({
if (fs.existsSync(proxyPath)) { if (fs.existsSync(proxyPath)) {
proxyConfig = require(proxyPath); proxyConfig = require(proxyPath);
} else { } else {
var message = 'Proxy config file ' + proxyPath + ' does not exist.'; const message = 'Proxy config file ' + proxyPath + ' does not exist.';
return Promise.reject(new SilentError(message)); return Promise.reject(new SilentError(message));
} }
} }
const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = { const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = {
contentBase: path.resolve(this.project.root, `./${CliConfig.fromProject().config.apps[0].root}`), contentBase: path.resolve(
this.project.root,
`./${CliConfig.fromProject().config.apps[0].root}`
),
historyApiFallback: true, historyApiFallback: true,
stats: webpackDevServerOutputOptions, stats: webpackDevServerOutputOptions,
inline: true, inline: true,
proxy: proxyConfig proxy: proxyConfig
}; };
const serveMessage:string = chalk.green(`\n*\n*\n NG Live Development Server is running on http://${commandOptions.host}:${commandOptions.port}.\n*\n*`); ui.writeLine(chalk.green(oneLine`
const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration); **
NG Live Development Server is running on
http://${commandOptions.host}:${commandOptions.port}.
**
`));
const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
server.listen(commandOptions.port, `${commandOptions.host}`, function(err, stats) { server.listen(commandOptions.port, `${commandOptions.host}`, function(err, stats) {
if(err) { if (err) {
lastHash = null;
console.error(err.stack || err); console.error(err.stack || err);
if(err.details) console.error(err.details); if (err.details) { console.error(err.details); }
reject(err.details); reject(err.details);
} }
if(stats && stats.hash && stats.hash !== lastHash) {
lastHash = stats.hash;
process.stdout.write(stats.toString(webpackOutputOptions) + '\n' + serveMessage + '\n');
}
process.stdout.write(serveMessage);
}); });
}) });
} }
}); });

View File

@ -1,7 +1,6 @@
import * as Promise from 'ember-cli/lib/ext/promise'; import * as Promise from 'ember-cli/lib/ext/promise';
import * as Task from 'ember-cli/lib/models/task'; import * as Task from 'ember-cli/lib/models/task';
import * as path from 'path'; import * as path from 'path';
import { getWebpackTestConfig } from '../models/webpack-build-test';
// require dependencies within the target project // require dependencies within the target project
function requireDependency(root, moduleName) { function requireDependency(root, moduleName) {
@ -10,7 +9,7 @@ function requireDependency(root, moduleName) {
return require(path.join(root, 'node_modules', moduleName, main)); return require(path.join(root, 'node_modules', moduleName, main));
} }
module.exports = Task.extend({ module.exports = Task.extend({
run: function (options) { run: function (options) {
const projectRoot = this.project.root; const projectRoot = this.project.root;
return new Promise((resolve) => { return new Promise((resolve) => {

View File

@ -14,6 +14,9 @@
"sourceRoot": "/", "sourceRoot": "/",
"target": "es5", "target": "es5",
"lib": ["es6"], "lib": ["es6"],
"typeRoots": [
"../../node_modules/@types"
],
"paths": { "paths": {
"@angular-cli/ast-tools": [ "../../packages/ast-tools/src" ] "@angular-cli/ast-tools": [ "../../packages/ast-tools/src" ]
} }

View File

@ -10,7 +10,7 @@ interface IWebpackPrerender {
export class PrerenderWebpackPlugin { export class PrerenderWebpackPlugin {
private bootloader: any; private bootloader: any;
private cachedTemplate: string private cachedTemplate: string;
constructor(private options: IWebpackPrerender) { constructor(private options: IWebpackPrerender) {
// maintain your platform instance // maintain your platform instance
@ -53,4 +53,4 @@ export class PrerenderWebpackPlugin {
delete require.cache[key]; delete require.cache[key];
}); });
} }
} };

View File

@ -15,8 +15,10 @@
"test:cli": "node tests/runner", "test:cli": "node tests/runner",
"test:inspect": "node --inspect --debug-brk tests/runner", "test:inspect": "node --inspect --debug-brk tests/runner",
"test:packages": "node scripts/run-packages-spec.js", "test:packages": "node scripts/run-packages-spec.js",
"lint": "eslint .", "build-config-interface": "dtsgen lib/config/schema.json --out lib/config/schema.d.ts",
"build-config-interface": "dtsgen lib/config/schema.json --out lib/config/schema.d.ts" "eslint": "eslint .",
"tslint": "tslint \"**/*.ts\" -c tslint.json -e \"**/blueprints/*/files/**/*.ts\" -e \"node_modules/**\" -e \"tmp/**\"",
"lint": "npm-run-all -c eslint tslint"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -41,6 +43,7 @@
"angular2-template-loader": "^0.5.0", "angular2-template-loader": "^0.5.0",
"awesome-typescript-loader": "^2.2.1", "awesome-typescript-loader": "^2.2.1",
"chalk": "^1.1.3", "chalk": "^1.1.3",
"common-tags": "^1.3.1",
"compression-webpack-plugin": "^0.3.1", "compression-webpack-plugin": "^0.3.1",
"copy-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^3.0.1",
"core-js": "^2.4.0", "core-js": "^2.4.0",
@ -66,6 +69,7 @@
"lodash": "^4.11.1", "lodash": "^4.11.1",
"node-sass": "^3.7.0", "node-sass": "^3.7.0",
"npm": "3.10.2", "npm": "3.10.2",
"npm-run-all": "^3.0.0",
"offline-plugin": "^3.4.1", "offline-plugin": "^3.4.1",
"opn": "4.0.1", "opn": "4.0.1",
"parse5": "^2.1.5", "parse5": "^2.1.5",

View File

@ -42,7 +42,7 @@ export function getSourceNodes(sourceFile: ts.SourceFile): Observable<ts.Node> {
const subject = new ReplaySubject<ts.Node>(); const subject = new ReplaySubject<ts.Node>();
let nodes: ts.Node[] = [sourceFile]; let nodes: ts.Node[] = [sourceFile];
while(nodes.length > 0) { while (nodes.length > 0) {
const node = nodes.shift(); const node = nodes.shift();
if (node) { if (node) {
@ -82,7 +82,7 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number {
*/ */
export function insertAfterLastOccurrence(nodes: ts.Node[], toInsert: string, export function insertAfterLastOccurrence(nodes: ts.Node[], toInsert: string,
file: string, fallbackPos?: number, syntaxKind?: ts.SyntaxKind): Change { file: string, fallbackPos?: number, syntaxKind?: ts.SyntaxKind): Change {
var lastItem = nodes.sort(nodesByPosition).pop(); let lastItem = nodes.sort(nodesByPosition).pop();
if (syntaxKind) { if (syntaxKind) {
lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop(); lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
} }
@ -99,7 +99,7 @@ export function getContentOfKeyLiteral(source: ts.SourceFile, node: ts.Node): st
return (<ts.Identifier>node).text; return (<ts.Identifier>node).text;
} else if (node.kind == ts.SyntaxKind.StringLiteral) { } else if (node.kind == ts.SyntaxKind.StringLiteral) {
try { try {
return JSON.parse(node.getFullText(source)) return JSON.parse(node.getFullText(source));
} catch (e) { } catch (e) {
return null; return null;
} }

View File

@ -65,7 +65,7 @@ export class MultiChange implements Change {
return this._changes return this._changes
.sort((a: Change, b: Change) => b.order - a.order) .sort((a: Change, b: Change) => b.order - a.order)
.reduce((promise, change) => { .reduce((promise, change) => {
return promise.then(() => change.apply()) return promise.then(() => change.apply());
}, Promise.resolve()); }, Promise.resolve());
} }
} }

View File

@ -168,7 +168,9 @@ describe('route utils', () => {
}); });
}); });
it('does not add provideRouter to bootstrap if present', () => { it('does not add provideRouter to bootstrap if present', () => {
let editedFile = new InsertChange(mainFile, 124, ', [ HTTP_PROVIDERS, provideRouter(routes) ]'); let editedFile = new InsertChange(mainFile,
124,
', [ HTTP_PROVIDERS, provideRouter(routes) ]');
return editedFile.apply() return editedFile.apply()
.then(() => nru.applyChanges(nru.bootstrapItem(mainFile, routes, toBootstrap))) .then(() => nru.applyChanges(nru.bootstrapItem(mainFile, routes, toBootstrap)))
.then(() => readFile(mainFile, 'utf8')) .then(() => readFile(mainFile, 'utf8'))
@ -203,14 +205,14 @@ describe('route utils', () => {
.then(() => readFile(mainFile, 'utf8')) .then(() => readFile(mainFile, 'utf8'))
.then(content => { .then(content => {
expect(content).toEqual(prefix + routerImport + expect(content).toEqual(prefix + routerImport +
'bootstrap(AppComponent, [ provideRouter(routes) ]);\n if(e){bootstrap, provideRouter});'); 'bootstrap(AppComponent, [ provideRouter(routes) ]);\n if(e){bootstrap, provideRouter});'); // tslint:disable-line
}); });
}); });
}); });
describe('addPathToRoutes', () => { describe('addPathToRoutes', () => {
const routesFile = 'src/routes.ts'; const routesFile = 'src/routes.ts';
var options = {dir: 'src/app', appRoot: 'src/app', routesFile: routesFile, let options = {dir: 'src/app', appRoot: 'src/app', routesFile: routesFile,
component: 'NewRouteComponent', dasherizedName: 'new-route'}; component: 'NewRouteComponent', dasherizedName: 'new-route'};
const nestedRoutes = `\n { path: 'home', component: HomeComponent, const nestedRoutes = `\n { path: 'home', component: HomeComponent,
children: [ children: [
@ -235,7 +237,8 @@ describe('route utils', () => {
}); });
it('adds import to new route component if absent', () => { it('adds import to new route component if absent', () => {
return nru.applyChanges(nru.addPathToRoutes(routesFile, _.merge({route: 'new-route'}, options))) return nru.applyChanges(nru.addPathToRoutes(routesFile,
_.merge({route: 'new-route'}, options)))
.then(() => readFile(routesFile, 'utf8')) .then(() => readFile(routesFile, 'utf8'))
.then(content => { .then(content => {
expect(content).toEqual( expect(content).toEqual(
@ -289,6 +292,7 @@ export default [\n { path: 'new-route', component: NewRouteComponent }\n];`);
nru.addPathToRoutes(routesFile, _.merge({route: 'home/about/details'}, options))); }) nru.addPathToRoutes(routesFile, _.merge({route: 'home/about/details'}, options))); })
.then(() => readFile(routesFile, 'utf8')) .then(() => readFile(routesFile, 'utf8'))
.then(content => { .then(content => {
// tslint:disable-next-line
let expected = `import { DetailsComponent } from './app/home/about/details/details.component'; let expected = `import { DetailsComponent } from './app/home/about/details/details.component';
export default [ export default [
{ path: 'home', component: HomeComponent, { path: 'home', component: HomeComponent,
@ -310,9 +314,11 @@ export default [
options.dasherizedName = 'sections'; options.dasherizedName = 'sections';
options.component = 'SectionsComponent'; options.component = 'SectionsComponent';
return nru.applyChanges( return nru.applyChanges(
nru.addPathToRoutes(routesFile, _.merge({route: 'home/about/more/sections'}, options))); }) nru.addPathToRoutes(routesFile,
_.merge({route: 'home/about/more/sections'}, options))); })
.then(() => readFile(routesFile, 'utf8')) .then(() => readFile(routesFile, 'utf8'))
.then(content => { .then(content => {
// tslint:disable-next-line
let expected = `import { SectionsComponent } from './app/home/about/more/sections/sections.component'; let expected = `import { SectionsComponent } from './app/home/about/more/sections/sections.component';
export default [ export default [
{ path: 'home', component: HomeComponent, { path: 'home', component: HomeComponent,
@ -347,6 +353,7 @@ export default [
nru.addPathToRoutes(routesFile, _.merge({route: 'home/about/:id'}, options))); }) nru.addPathToRoutes(routesFile, _.merge({route: 'home/about/:id'}, options))); })
.then(() => readFile(routesFile, 'utf8')) .then(() => readFile(routesFile, 'utf8'))
.then(content => { .then(content => {
// tslint:disable-next-line
expect(content).toEqual(`import { AboutComponent_1 } from './app/home/about/about.component'; expect(content).toEqual(`import { AboutComponent_1 } from './app/home/about/about.component';
export default [ export default [
{ path: 'main', component: MainComponent } { path: 'main', component: MainComponent }
@ -435,6 +442,7 @@ export default [
}) })
.then(() => readFile(routesFile, 'utf8')) .then(() => readFile(routesFile, 'utf8'))
.then(content => { .then(content => {
// tslint:disable-next-line
let expected = `import { TrapQueenComponent } from './app/home/trap-queen/trap-queen.component'; let expected = `import { TrapQueenComponent } from './app/home/trap-queen/trap-queen.component';
export default [ export default [
{ path: 'home', component: HomeComponent, { path: 'home', component: HomeComponent,
@ -489,7 +497,7 @@ export default [
let editedFile = new InsertChange(routesFile, 0, let editedFile = new InsertChange(routesFile, 0,
`import { AboutComponent } from './app/about/about.component'; `import { AboutComponent } from './app/about/about.component';
import { DetailsComponent } from './app/about/details/details.component'; import { DetailsComponent } from './app/about/details/details.component';
import { DetailsComponent as DetailsComponent_1 } from './app/about/description/details.component;\n`); import { DetailsComponent as DetailsComponent_1 } from './app/about/description/details.component;\n`); // tslint:disable-line
return editedFile.apply(); return editedFile.apply();
}).then(() => { }).then(() => {
options.dasherizedName = 'details'; options.dasherizedName = 'details';

View File

@ -12,9 +12,13 @@ import {insertAfterLastOccurrence} from './ast-utils';
* @param imports Object { importedClass: ['path/to/import/from', defaultStyleImport?] } * @param imports Object { importedClass: ['path/to/import/from', defaultStyleImport?] }
* @param toBootstrap * @param toBootstrap
*/ */
export function bootstrapItem(mainFile: string, imports: {[key: string]: (string | boolean)[]}, toBootstrap: string ) { export function bootstrapItem(
mainFile: string,
imports: {[key: string]: (string | boolean)[]},
toBootstrap: string
) {
let changes = Object.keys(imports).map(importedClass => { let changes = Object.keys(imports).map(importedClass => {
var defaultStyleImport = imports[importedClass].length === 2 && !!imports[importedClass][1]; let defaultStyleImport = imports[importedClass].length === 2 && !!imports[importedClass][1];
return insertImport( return insertImport(
mainFile, mainFile,
importedClass, importedClass,
@ -43,8 +47,8 @@ export function bootstrapItem(mainFile: string, imports: {[key: string]: (string
} }
// if bracket exitst already, add configuration template, // if bracket exitst already, add configuration template,
// otherwise, insert into bootstrap parens // otherwise, insert into bootstrap parens
var fallBackPos: number, configurePathsTemplate: string, separator: string; let fallBackPos: number, configurePathsTemplate: string, separator: string;
var syntaxListNodes: any; let syntaxListNodes: any;
let bootstrapProviders = bootstrapNode.getChildAt(2).getChildAt(2); // array of providers let bootstrapProviders = bootstrapNode.getChildAt(2).getChildAt(2); // array of providers
if ( bootstrapProviders ) { if ( bootstrapProviders ) {
@ -88,7 +92,7 @@ export function insertImport(fileToEdit: string, symbolName: string,
if (relevantImports.length > 0) { if (relevantImports.length > 0) {
var importsAsterisk = false; let importsAsterisk = false;
// imports from import file // imports from import file
let imports: ts.Node[] = []; let imports: ts.Node[] = [];
relevantImports.forEach(n => { relevantImports.forEach(n => {
@ -128,7 +132,13 @@ export function insertImport(fileToEdit: string, symbolName: string,
let separator = insertAtBeginning ? '' : ';\n'; let separator = insertAtBeginning ? '' : ';\n';
let toInsert = `${separator}import ${open}${symbolName}${close}` + let toInsert = `${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`; ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral); return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral
);
}; };
/** /**
@ -149,9 +159,13 @@ export function addPathToRoutes(routesFile: string, pathOptions: any): Change[]
let routePath = route.replace(positionalRoutes, ''); let routePath = route.replace(positionalRoutes, '');
routePath = `./app/${routePath}/${pathOptions.dasherizedName}.component`; routePath = `./app/${routePath}/${pathOptions.dasherizedName}.component`;
let originalComponent = pathOptions.component; let originalComponent = pathOptions.component;
pathOptions.component = resolveImportName(pathOptions.component, routePath, pathOptions.routesFile); pathOptions.component = resolveImportName(
pathOptions.component,
routePath,
pathOptions.routesFile
);
var content = `{ path: '${route}', component: ${pathOptions.component}${isDefault}${outlet} }`; let content = `{ path: '${route}', component: ${pathOptions.component}${isDefault}${outlet} }`;
let rootNode = getRootNode(routesFile); let rootNode = getRootNode(routesFile);
let routesNode = rootNode.getChildAt(0).getChildren().filter(n => { let routesNode = rootNode.getChildAt(0).getChildren().filter(n => {
// get export statement // get export statement
@ -162,7 +176,7 @@ export function addPathToRoutes(routesFile: string, pathOptions: any): Change[]
throw new Error('Did not insert path in routes.ts because ' + throw new Error('Did not insert path in routes.ts because ' +
`there were multiple or no 'export default' statements`); `there were multiple or no 'export default' statements`);
} }
var pos = routesNode[0].getChildAt(2).getChildAt(0).end; // openBracketLiteral let pos = routesNode[0].getChildAt(2).getChildAt(0).end; // openBracketLiteral
// all routes in export route array // all routes in export route array
let routesArray = routesNode[0].getChildAt(2).getChildAt(1) let routesArray = routesNode[0].getChildAt(2).getChildAt(1)
.getChildren() .getChildren()
@ -172,14 +186,16 @@ export function addPathToRoutes(routesFile: string, pathOptions: any): Change[]
// don't duplicate routes // don't duplicate routes
throw new Error('Route was not added since it is a duplicate'); throw new Error('Route was not added since it is a duplicate');
} }
var isChild = false; let isChild = false;
// get parent to insert under // get parent to insert under
let parent: ts.Node; let parent: ts.Node;
if (pathOptions.parent) { if (pathOptions.parent) {
// append '_' to route to find the actual parent (not parent of the parent) // append '_' to route to find the actual parent (not parent of the parent)
parent = getParent(routesArray, `${pathOptions.parent}/_`); parent = getParent(routesArray, `${pathOptions.parent}/_`);
if (!parent) { if (!parent) {
throw new Error(`You specified parent '${pathOptions.parent}'' which was not found in routes.ts`); throw new Error(
`You specified parent '${pathOptions.parent}'' which was not found in routes.ts`
);
} }
if (route.indexOf(pathOptions.parent) === 0) { if (route.indexOf(pathOptions.parent) === 0) {
route = route.substring(pathOptions.parent.length); route = route.substring(pathOptions.parent.length);
@ -296,8 +312,8 @@ function resolveImportName (importName: string, importPath: string, fileName: st
return importName; return importName;
} }
const baseName = importNames[index].split('_')[0]; const baseName = importNames[index].split('_')[0];
var newName = baseName; let newName = baseName;
var resolutionNumber = 1; let resolutionNumber = 1;
while (importNames.indexOf(newName) !== -1) { while (importNames.indexOf(newName) !== -1) {
newName = `${baseName}_${resolutionNumber}`; newName = `${baseName}_${resolutionNumber}`;
resolutionNumber++; resolutionNumber++;
@ -328,8 +344,9 @@ export function resolveComponentPath(projectRoot: string, currentDir: string, fi
// only component file name is given // only component file name is given
filePath = componentName; filePath = componentName;
} }
var directory = filePath[0] === path.sep ? let directory = filePath[0] === path.sep ?
path.resolve(path.join(projectRoot, 'src', 'app', filePath)) : path.resolve(currentDir, filePath); path.resolve(path.join(projectRoot, 'src', 'app', filePath)) :
path.resolve(currentDir, filePath);
if (!fs.existsSync(directory)) { if (!fs.existsSync(directory)) {
throw new Error(`path '${filePath}' must be relative to current directory` + throw new Error(`path '${filePath}' must be relative to current directory` +
@ -364,13 +381,14 @@ function addChildPath (parentObject: ts.Node, pathOptions: any, route: string) {
if (!parentObject) { if (!parentObject) {
return; return;
} }
var pos: number; let pos: number;
var newContent: string; let newContent: string;
// get object with 'children' property // get object with 'children' property
let childrenNode = parentObject.getChildAt(1).getChildren() let childrenNode = parentObject.getChildAt(1).getChildren()
.filter(n => n.kind === ts.SyntaxKind.PropertyAssignment .filter(n =>
&& ((n as ts.PropertyAssignment).name as ts.Identifier).text === 'children'); n.kind === ts.SyntaxKind.PropertyAssignment
&& ((n as ts.PropertyAssignment).name as ts.Identifier).text === 'children');
// find number of spaces to pad nested paths // find number of spaces to pad nested paths
let nestingLevel = 1; // for indenting route object in the `children` array let nestingLevel = 1; // for indenting route object in the `children` array
let n = parentObject; let n = parentObject;
@ -417,7 +435,7 @@ function getParent(routesArray: ts.Node[], route: string, parent?: ts.Node): ts.
if (route.length === 0) { if (route.length === 0) {
return; // route has been completely matched return; // route has been completely matched
} }
var splitRoute = route.split('/'); let splitRoute = route.split('/');
// don't treat positional parameters separately // don't treat positional parameters separately
if (splitRoute.length > 1 && splitRoute[1].indexOf(':') !== -1) { if (splitRoute.length > 1 && splitRoute[1].indexOf(':') !== -1) {
let actualRoute = splitRoute.shift(); let actualRoute = splitRoute.shift();
@ -442,13 +460,18 @@ function getParent(routesArray: ts.Node[], route: string, parent?: ts.Node): ts.
* Helper for addPathToRoutes. * Helper for addPathToRoutes.
* @return whether path with same route and component exists * @return whether path with same route and component exists
*/ */
function pathExists(routesArray: ts.Node[], route: string, component: string, fullRoute?: string): boolean { function pathExists(
routesArray: ts.Node[],
route: string,
component: string,
fullRoute?: string
): boolean {
if (routesArray.length === 0) { if (routesArray.length === 0) {
return false; return false;
} }
fullRoute = fullRoute ? fullRoute : route; fullRoute = fullRoute ? fullRoute : route;
var sameRoute = false; let sameRoute = false;
var splitRoute = route.split('/'); let splitRoute = route.split('/');
// don't treat positional parameters separately // don't treat positional parameters separately
if (splitRoute.length > 1 && splitRoute[1].indexOf(':') !== -1) { if (splitRoute.length > 1 && splitRoute[1].indexOf(':') !== -1) {
let actualRoute = splitRoute.shift(); let actualRoute = splitRoute.shift();
@ -461,7 +484,7 @@ function pathExists(routesArray: ts.Node[], route: string, component: string, fu
sameRoute = currentRoute === splitRoute[0]; sameRoute = currentRoute === splitRoute[0];
// Confirm that it's parents are the same // Confirm that it's parents are the same
if (sameRoute && sameComponent) { if (sameRoute && sameComponent) {
var path = currentRoute; let path = currentRoute;
let objExp = n.parent; let objExp = n.parent;
while (objExp) { while (objExp) {
if (objExp.kind === ts.SyntaxKind.ObjectLiteralExpression) { if (objExp.kind === ts.SyntaxKind.ObjectLiteralExpression) {

View File

@ -130,7 +130,8 @@ describe('ModuleResolver', () => {
.then((tsFile: ts.SourceFile) => { .then((tsFile: ts.SourceFile) => {
let contentsBaz = dependentFilesUtils.getImportClauses(tsFile); let contentsBaz = dependentFilesUtils.getImportClauses(tsFile);
let barExpectedContent = path.normalize('../bar/bar.component'); let barExpectedContent = path.normalize('../bar/bar.component');
let fooBarExpectedContent = `.${path.sep}qux${path.sep}quux${path.sep}foobar${path.sep}foobar.component`; let fooBarExpectedContent =
`.${path.sep}qux${path.sep}quux${path.sep}foobar${path.sep}foobar.component`;
expect(contentsBaz[0].specifierText).to.equal(barExpectedContent); expect(contentsBaz[0].specifierText).to.equal(barExpectedContent);
expect(contentsBaz[1].specifierText).to.equal(fooBarExpectedContent); expect(contentsBaz[1].specifierText).to.equal(fooBarExpectedContent);
}); });

View File

@ -18,9 +18,9 @@
"no-internal-module": true, "no-internal-module": true,
"no-trailing-whitespace": true, "no-trailing-whitespace": true,
"no-bitwise": true, "no-bitwise": true,
"no-shadowed-variable": true,
"no-unused-expression": true, "no-unused-expression": true,
"no-unused-variable": true, "no-unused-variable": true,
"no-var-keyword": true,
"one-line": [ "one-line": [
true, true,
"check-catch", "check-catch",
@ -49,7 +49,8 @@
true, true,
"ban-keywords", "ban-keywords",
"check-format", "check-format",
"allow-trailing-underscore" "allow-leading-underscore",
"allow-pascal-case"
], ],
"whitespace": [ "whitespace": [
true, true,