diff --git a/.appveyor.yml b/.appveyor.yml index 26f8d6968d55..ec90cf597337 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,7 @@ environment: - nodejs_version: "5.0" + matrix: + - nodejs_version: "5.0" + - nodejs_version: "6.0" install: - ps: Install-Product node $env:nodejs_version diff --git a/.travis.yml b/.travis.yml index 874ac8c166df..f4f5e0bd6d67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ env: - NODE_VERSION=5 SCRIPT=lint - NODE_VERSION=5 SCRIPT=test - NODE_VERSION=5 TARGET=mobile SCRIPT=mobile_test + - NODE_VERSION=6 SCRIPT=test + - NODE_VERSION=6 TARGET=mobile SCRIPT=mobile_test os: - linux - osx @@ -16,6 +18,8 @@ matrix: env: NODE_VERSION=5 SCRIPT=lint - os: osx env: NODE_VERSION=5 TARGET=mobile SCRIPT=mobile_test + - os: osx + env: NODE_VERSION=6 TARGET=mobile SCRIPT=mobile_test script: - npm run-script $SCRIPT diff --git a/CHANGELOG.md b/CHANGELOG.md index 07567ddd5cb3..039ba99e2802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ + +# [1.0.0-beta.10](https://github.com/angular/angular-cli/compare/1.0.0-beta.9...v1.0.0-beta.10) (2016-07-19) + + +### Bug Fixes + +* **build:** don't ignore js in public ([#1129](https://github.com/angular/angular-cli/issues/1129)) ([00e111a](https://github.com/angular/angular-cli/commit/00e111a)), closes [#540](https://github.com/angular/angular-cli/issues/540) +* **mobile:** remove app/index.js from concatenated bundle ([#1267](https://github.com/angular/angular-cli/issues/1267)) ([03fd4c4](https://github.com/angular/angular-cli/commit/03fd4c4)) +* Fix all versions of dependencies to Angular-CLI ([#1331](https://github.com/angular/angular-cli/issues/1331)) ([022e7f9](https://github.com/angular/angular-cli/commit/022e7f9)), closes [#1331](https://github.com/angular/angular-cli/issues/1331) +* fix versions in the shrinkwrap instead of using ranges ([#1350](https://github.com/angular/angular-cli/issues/1350)) ([72bc9d9](https://github.com/angular/angular-cli/commit/72bc9d9)), closes [#1350](https://github.com/angular/angular-cli/issues/1350) + + +### Features + +* **router:** upgrade the router version ([#1288](https://github.com/angular/angular-cli/issues/1288)) ([2c9a371](https://github.com/angular/angular-cli/commit/2c9a371)) +* add get-dependent-fils utils ([6590743](https://github.com/angular/angular-cli/commit/6590743)) + + + **Always follow the [update guide](https://github.com/angular/angular-cli/blob/master/README.md#updating-angular-cli) when updating to a new version. The changelog does not list breaking changes that are fixed via the update procedure.** --- diff --git a/README.md b/README.md index 2deb864fa7b2..a91d41328aec 100644 --- a/README.md +++ b/README.md @@ -102,25 +102,9 @@ Enum | `ng g enum my-new-enum` ### Generating a route -You can generate a new route with the following command (note the singular -used in `hero`): +Generating routes in the CLI has been disabled for the time being. A new router and new route generation blueprints are coming. -```bash -ng generate route hero -``` - -This will create a folder which will contain the hero component and related test and style files. - -The generated route will also be registered with the parent component's `@RouteConfig` decorator. - -By default the route will be designated as a **lazy** route which means that it will be loaded into the browser when needed, not upfront as part of a bundle. - -In order to visually distinguish lazy routes from other routes the folder for the route will be prefixed with a `+` per the above example the folder will be named `+hero`. -This is done in accordance with the [style guide](https://angular.io/styleguide#!#prefix-lazy-loaded-folders-with-). - -The default lazy nature of routes can be turned off via the lazy flag (`--lazy false`) - -There is an optional flag for `skip-router-generation` which will not add the route to the parent component's `@RouteConfig` decorator. +You can read the official documentation for the new Router here: https://angular.io/docs/ts/latest/guide/router.html. Please note that even though route generation is disabled, building your projects with routing is still fully supported. ### Creating a build diff --git a/addon/ng2/blueprints/ng2/files/__path__/system-config.ts b/addon/ng2/blueprints/ng2/files/__path__/system-config.ts index 55e4ac712886..f15ddf871001 100644 --- a/addon/ng2/blueprints/ng2/files/__path__/system-config.ts +++ b/addon/ng2/blueprints/ng2/files/__path__/system-config.ts @@ -1,3 +1,5 @@ +"use strict"; + // SystemJS configuration file, see links for more information // https://github.com/systemjs/systemjs // https://github.com/systemjs/systemjs/blob/master/docs/config-api.md diff --git a/addon/ng2/blueprints/ng2/files/__path__/typings.d.ts b/addon/ng2/blueprints/ng2/files/__path__/typings.d.ts index a58a428505dd..96dd4854d801 100644 --- a/addon/ng2/blueprints/ng2/files/__path__/typings.d.ts +++ b/addon/ng2/blueprints/ng2/files/__path__/typings.d.ts @@ -2,5 +2,5 @@ // https://github.com/typings/typings // https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html -/// +/// <% if(!isMobile) { %>declare var module: { id: string };<% } %> diff --git a/addon/ng2/blueprints/ng2/files/e2e/typings.d.ts b/addon/ng2/blueprints/ng2/files/e2e/typings.d.ts index 9c2f2d0252ef..eebc2728b868 100644 --- a/addon/ng2/blueprints/ng2/files/e2e/typings.d.ts +++ b/addon/ng2/blueprints/ng2/files/e2e/typings.d.ts @@ -1 +1 @@ -/// +/// diff --git a/addon/ng2/blueprints/ng2/files/package.json b/addon/ng2/blueprints/ng2/files/package.json index 9c58713a4b9f..01ada2be0e42 100644 --- a/addon/ng2/blueprints/ng2/files/package.json +++ b/addon/ng2/blueprints/ng2/files/package.json @@ -47,6 +47,6 @@ "ts-node": "0.5.5", "tslint": "3.11.0", "typescript": "1.8.10", - "typings": "0.8.1"<%= stylePackage %> + "typings": "1.3.1"<%= stylePackage %> } } diff --git a/addon/ng2/blueprints/ng2/files/typings.json b/addon/ng2/blueprints/ng2/files/typings.json index 21f9888ab067..dc0f75bd2140 100644 --- a/addon/ng2/blueprints/ng2/files/typings.json +++ b/addon/ng2/blueprints/ng2/files/typings.json @@ -1,11 +1,11 @@ { - "ambientDevDependencies": { + "globalDevDependencies": { "angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459", - "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", + "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654" }, - "ambientDependencies": { - "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654"<% if (isMobile) {%>, - "node": "registry:dt/node#4.0.0+20160509154515" <% } %> + "globalDependencies": { + "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504"<% if (isMobile) {%>, + "node": "registry:dt/node#6.0.0+20160709114037" <% } %> } } diff --git a/addon/ng2/utilities/get-dependent-files.ts b/addon/ng2/utilities/get-dependent-files.ts new file mode 100644 index 000000000000..6f38efd4dfcf --- /dev/null +++ b/addon/ng2/utilities/get-dependent-files.ts @@ -0,0 +1,124 @@ +'use strict'; + +import * as fs from 'fs'; +import * as ts from 'typescript'; +import * as glob from 'glob'; +import * as path from 'path'; +import * as denodeify from 'denodeify'; + +import { Promise } from 'es6-promise'; + +/** + * Interface that represents a module specifier and its position in the source file. + * Use for storing a string literal, start position and end posittion of ImportClause node kinds. + */ +export interface ModuleImport { + specifierText: string; + pos: number; + end: number; +}; + +export interface ModuleMap { + [key: string]: ModuleImport[]; +} + +/** + * Create a SourceFile as defined by Typescript Compiler API. + * Generate a AST structure from a source file. + * + * @param fileName source file for which AST is to be extracted + */ +export function createTsSourceFile(fileName: string): Promise { + const readFile = denodeify(fs.readFile); + return readFile(fileName, 'utf8') + .then((contents: string) => { + return ts.createSourceFile(fileName, contents, ts.ScriptTarget.ES6, true); + }); +} + +/** + * Traverses through AST of a given file of kind 'ts.SourceFile', filters out child + * nodes of the kind 'ts.SyntaxKind.ImportDeclaration' and returns import clauses as + * ModuleImport[] + * + * @param {ts.SourceFile} node: Typescript Node of whose AST is being traversed + * + * @return {ModuleImport[]} traverses through ts.Node and returns an array of moduleSpecifiers. + */ +export function getImportClauses(node: ts.SourceFile): ModuleImport[] { + return node.statements + .filter(node => node.kind === ts.SyntaxKind.ImportDeclaration) // Only Imports. + .map((node: ts.ImportDeclaration) => { + let moduleSpecifier = node.moduleSpecifier; + return { + specifierText: moduleSpecifier.getText().slice(1, -1), + pos: moduleSpecifier.pos, + end: moduleSpecifier.end + }; + }); +} + +/** + * Find the file, 'index.ts' given the directory name and return boolean value + * based on its findings. + * + * @param dirPath + * + * @return a boolean value after it searches for a barrel (index.ts by convention) in a given path + */ +export function hasIndexFile(dirPath: string): Promise { + const globSearch = denodeify(glob); + return globSearch(path.join(dirPath, 'index.ts'), { nodir: true }) + .then((indexFile: string[]) => { + return indexFile.length > 0; + }); +} + +/** + * Returns a map of all dependent file/s' path with their moduleSpecifier object + * (specifierText, pos, end) + * + * @param fileName file upon which other files depend + * @param rootPath root of the project + * + * @return {Promise} ModuleMap of all dependent file/s (specifierText, pos, end) + * + */ +export function getDependentFiles(fileName: string, rootPath: string): Promise { + const globSearch = denodeify(glob); + return globSearch(path.join(rootPath, '**/*.*.ts'), { nodir: true }) + .then((files: string[]) => Promise.all(files.map(file => createTsSourceFile(file))) + .then((tsFiles: ts.SourceFile[]) => tsFiles.map(file => getImportClauses(file))) + .then((moduleSpecifiers: ModuleImport[][]) => { + let allFiles: ModuleMap = {}; + files.forEach((file, index) => { + let sourcePath = path.normalize(file); + allFiles[sourcePath] = moduleSpecifiers[index]; + }); + return allFiles; + }) + .then((allFiles: ModuleMap) => { + let relevantFiles: ModuleMap = {}; + Object.keys(allFiles).forEach(filePath => { + const tempModuleSpecifiers: ModuleImport[] = allFiles[filePath] + .filter(importClause => { + // Filter only relative imports + let singleSlash = importClause.specifierText.charAt(0) === '/'; + let currentDirSyntax = importClause.specifierText.slice(0, 2) === './'; + let parentDirSyntax = importClause.specifierText.slice(0, 3) === '../'; + return singleSlash || currentDirSyntax || parentDirSyntax; + }) + .filter(importClause => { + let modulePath = path.resolve(path.dirname(filePath), importClause.specifierText); + let resolvedFileName = path.resolve(fileName); + let fileBaseName = path.basename(resolvedFileName, '.ts'); + let parsedFilePath = path.join(path.dirname(resolvedFileName), fileBaseName); + return (parsedFilePath === modulePath) || (resolvedFileName === modulePath); + }); + if (tempModuleSpecifiers.length > 0) { + relevantFiles[filePath] = tempModuleSpecifiers; + }; + }); + return relevantFiles; + })); +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 5fc2d093f6fb..c7ddd786a527 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1104,6 +1104,10 @@ "version": "1.0.0", "from": "delegates@1.0.0" }, + "denodeify": { + "version": "1.2.1", + "from": "denodeify@>=1.2.1 <2.0.0" + }, "depd": { "version": "1.1.0", "from": "depd@1.1.0" @@ -2346,6 +2350,10 @@ "version": "0.1.4", "from": "es6-map@0.1.4" }, + "es6-promise": { + "version": "3.2.1", + "from": "es6-promise@>=3.2.1 <4.0.0" + }, "es6-set": { "version": "0.1.4", "from": "es6-set@0.1.4" diff --git a/package.json b/package.json index 4aafdb5f276e..2d6c3ed36e21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-cli", - "version": "1.0.0-beta.9", + "version": "1.0.0-beta.10", "description": "CLI tool for Angular", "main": "lib/cli/index.js", "trackingCode": "UA-8594346-19", @@ -40,8 +40,10 @@ "broccoli-uglify-js": "^0.1.3", "broccoli-writer": "^0.1.1", "chalk": "^1.1.3", + "denodeify": "^1.2.1", "ember-cli": "2.5.0", "ember-cli-string-utils": "^1.0.0", + "es6-promise": "^3.2.1", "exit": "^0.1.2", "fs-extra": "^0.30.0", "glob": "^7.0.3", diff --git a/tests/acceptance/get-dependent-files.spec.ts b/tests/acceptance/get-dependent-files.spec.ts new file mode 100644 index 000000000000..ff36244a0971 --- /dev/null +++ b/tests/acceptance/get-dependent-files.spec.ts @@ -0,0 +1,154 @@ +'use strict'; + +// This needs to be first so fs module can be mocked correctly. +let mockFs = require('mock-fs'); +import { expect, assert } from 'chai'; +import * as path from 'path'; +import * as ts from 'typescript'; +import * as dependentFilesUtils from '../../addon/ng2/utilities/get-dependent-files'; + +describe('Get Dependent Files: ', () => { + let rootPath = 'src/app'; + + beforeEach(() => { + let mockDrive = { + 'src/app': { + 'foo': { + 'foo.component.ts': `import * from '../bar/baz/baz.component' + import * from '../bar/bar.component'`, + 'index.ts': `export * from './foo.component'` + }, + 'bar': { + 'baz': { + 'baz.component.ts': 'import * from "../bar.component"', + 'baz.html': '

Hello

' + }, + 'bar.component.ts': `import * from './baz/baz.component' + import * from '../foo'` + }, + 'foo-baz': { + 'no-module.component.ts': '' + }, + 'empty-dir': {} + } + }; + mockFs(mockDrive); + }); + afterEach(() => { + mockFs.restore(); + }); + + describe('getImportClauses', () => { + it('returns import specifiers when there is a single import statement', () => { + let sourceFile = path.join(rootPath, 'bar/baz/baz.component.ts'); + return dependentFilesUtils.createTsSourceFile(sourceFile) + .then((tsFile: ts.SourceFile) => { + let contents = dependentFilesUtils.getImportClauses(tsFile); + let expectedContents = [{ + specifierText: '../bar.component', + pos: 13, + end: 32 + }]; + assert.deepEqual(contents, expectedContents); + }); + }); + it('returns imports specifiers when there are multiple import statements', () => { + let sourceFile = path.join(rootPath, 'foo/foo.component.ts'); + return dependentFilesUtils.createTsSourceFile(sourceFile) + .then((tsFile: ts.SourceFile) => { + let contents = dependentFilesUtils.getImportClauses(tsFile); + let expectedContents = [ + { + specifierText: '../bar/baz/baz.component', + pos: 13, + end: 40 + }, + { + specifierText: '../bar/bar.component', + pos: 85, + end: 108 + } + ]; + assert.deepEqual(contents, expectedContents); + }); + }); + }); + + describe('createTsSourceFile', () => { + it('creates ts.SourceFile give a file path', () => { + let sourceFile = path.join(rootPath, 'foo/foo.component.ts'); + return dependentFilesUtils.createTsSourceFile(sourceFile) + .then((tsFile: ts.SourceFile) => { + let isTsSourceFile = (tsFile.kind === ts.SyntaxKind.SourceFile); + expect(isTsSourceFile).to.be.true; + }); + }); + }); + + describe('hasIndexFile', () => { + it('returns true when there is a index file', () => { + let sourceFile = path.join(rootPath, 'foo'); + dependentFilesUtils.hasIndexFile(sourceFile) + .then((booleanValue: boolean) => { + expect(booleanValue).to.be.true; + }); + }); + it('returns false when there is no index file', () => { + let sourceFile = path.join(rootPath, 'bar'); + dependentFilesUtils.hasIndexFile(sourceFile) + .then((booleanValue: boolean) => { + expect(booleanValue).to.be.false; + }); + }); + }); + + describe('returns a map of all files which depend on a given file ', () => { + it('when the given component unit has no index file', () => { + let sourceFile = path.join(rootPath, 'bar/bar.component.ts'); + return dependentFilesUtils.getDependentFiles(sourceFile, rootPath) + .then((contents: dependentFilesUtils.ModuleMap) => { + let bazFile = path.join(rootPath, 'bar/baz/baz.component.ts'); + let fooFile = path.join(rootPath, 'foo/foo.component.ts'); + let expectedContents: dependentFilesUtils.ModuleMap = {}; + expectedContents[bazFile] = [{ + specifierText: '../bar.component', + pos: 13, + end: 32 + }]; + expectedContents[fooFile] = [{ + specifierText: '../bar/bar.component', + pos: 85, + end: 108 + }]; + assert.deepEqual(contents, expectedContents); + }); + }); + it('when the given component unit has no index file [More Test]', () => { + let sourceFile = path.join(rootPath, 'bar/baz/baz.component.ts'); + return dependentFilesUtils.getDependentFiles(sourceFile, rootPath) + .then((contents: dependentFilesUtils.ModuleMap) => { + let expectedContents: dependentFilesUtils.ModuleMap = {}; + let barFile = path.join(rootPath, 'bar/bar.component.ts'); + let fooFile = path.join(rootPath, 'foo/foo.component.ts'); + expectedContents[barFile] = [{ + specifierText: './baz/baz.component', + pos: 13, + end: 35 + }]; + expectedContents[fooFile] = [{ + specifierText: '../bar/baz/baz.component', + pos: 13, + end: 40 + }]; + assert.deepEqual(contents, expectedContents); + }); + }); + it('when there are no dependent files', () => { + let sourceFile = path.join(rootPath, 'foo-baz/no-module.component.ts'); + return dependentFilesUtils.getDependentFiles(sourceFile, rootPath) + .then((contents: dependentFilesUtils.ModuleMap) => { + assert.deepEqual(contents, {}); + }); + }); + }); +}); diff --git a/typings.json b/typings.json index e9093d1857fc..5d47c2fc7a0b 100644 --- a/typings.json +++ b/typings.json @@ -1,5 +1,7 @@ { - "dependencies": {}, + "dependencies": { + "es6-promise": "registry:npm/es6-promise#3.0.0+20160211003958" + }, "devDependencies": { "chalk": "github:typings/typed-chalk#a7e422c5455e70292e5675a727d43a7b05fc3e58" },