Compare commits

..

1 Commits

Author SHA1 Message Date
SteveSandersonMS
acdf18f19e Add example of using TypeScript 2.1 + Webpack 2.1 + paths to help with #494 2016-12-07 16:36:01 +00:00
97 changed files with 580 additions and 1385 deletions

View File

@@ -1,7 +1,5 @@
# JavaScriptServices
AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/gprilrckx116vc9m/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/javascriptservices/branch/dev)
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
## What is this?

View File

@@ -1,44 +1,7 @@
init:
- git config --global core.autocrlf true
install:
- ps: Install-Product node 6.9.2 x64
# .NET Core SDK binaries
# Download .NET Core SDK and add to PATH
- ps: $urlCurrent = "https://go.microsoft.com/fwlink/?LinkID=834991"
- ps: $urlPreview = "https://dotnetcli.blob.core.windows.net/dotnet/Sdk/rel-1.0.0/dotnet-dev-win-x64.latest.zip"
- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
- ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
- ps: $tempFileCurrent = [System.IO.Path]::GetTempFileName()
- ps: $tempFilePreview = [System.IO.Path]::GetTempFileName()
- ps: (New-Object System.Net.WebClient).DownloadFile($urlCurrent, $tempFileCurrent)
- ps: (New-Object System.Net.WebClient).DownloadFile($urlPreview, $tempFilePreview)
- ps: Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFileCurrent, $env:DOTNET_INSTALL_DIR)
- ps: Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFilePreview, $env:DOTNET_INSTALL_DIR + "preview")
- ps: Copy-Item "${env:DOTNET_INSTALL_DIR}preview\*" "${env:DOTNET_INSTALL_DIR}" -Force -Recurse
- ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
build_script:
- ps: Push-Location
- cd templates/package-builder
- npm install
- npm run build
- ps: Pop-Location
artifacts:
- path: templates\package-builder\dist\artifacts\generator-aspnetcore-spa.tar.gz
name: generator-aspnetcore-spa
# - build.cmd verify
- build.cmd verify
clone_depth: 1
test_script:
- dotnet restore ./src
- npm install -g selenium-standalone
- selenium-standalone install
# The nosys flag is needed for selenium to work on Appveyor
- ps: Start-Process selenium-standalone 'start','--','-Djna.nosys=true'
- ps: Push-Location
- cd test
- npm install
- npm test
on_finish :
- ps: Pop-Location
# After running tests, upload results to Appveyor
- ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\test\tmp\junit\*.xml))
test: off
deploy: off

View File

@@ -1,4 +1,3 @@
{
"projects": ["src"],
"sdk": { "version": "1.0.0-preview2-1-003177" }
"projects": ["src"]
}

View File

@@ -2,13 +2,7 @@
var VERSION='0.1'
var FULL_VERSION='0.1'
var AUTHORS='Microsoft Open Technologies, Inc.'
var TEST_PROJECT_GLOB='templates/*/project.json'
var SAMPLES_PROJECT_GLOB='samples/misc/*/project.json'
@{
// The build quality values are set in each of the project.json files
BuildQuality = "";
}
var SAMPLES_PROJECT_GLOB='templates/*/project.json'
use-standard-lifecycle
k-standard-goals

View File

@@ -38,8 +38,7 @@ namespace Webpack.ActionResults
request.GetEncodedUrl(),
request.Path + request.QueryString.Value,
_dataToSupply,
/* timeoutMilliseconds */ 30000,
/* requestPathBase */ "/"
/* timeoutMilliseconds */ 30000
);
response.ContentType = "text/html";

View File

@@ -1,4 +1,4 @@
import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer, Store as ReduxStore } from 'redux';
import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer } from 'redux';
import thunk from 'redux-thunk';
import { routerReducer } from 'react-router-redux';
import * as Store from './store';
@@ -16,7 +16,7 @@ export default function configureStore(initialState?: Store.ApplicationState) {
// Combine all reducers and instantiate the app-wide store instance
const allReducers = buildRootReducer(Store.reducers);
const store = createStoreWithMiddleware(allReducers, initialState) as ReduxStore<Store.ApplicationState>;
const store = createStoreWithMiddleware(allReducers, initialState) as Redux.Store<Store.ApplicationState>;
// Enable Webpack hot module replacement for reducers
if (module.hot) {

View File

@@ -2,7 +2,6 @@
"name": "music-store",
"version": "0.0.0",
"dependencies": {
"@types/history": "^2.0.0",
"@types/react": "^0.14.29",
"@types/react-bootstrap": "^0.0.35",
"@types/react-dom": "^0.14.14",
@@ -42,7 +41,7 @@
"redux-typed": "^2.0.0",
"style-loader": "^0.13.0",
"ts-loader": "^0.8.1",
"typescript": "^2.0.3",
"typescript": "2.0.3",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-hot-middleware": "^2.12.2",

View File

@@ -1,19 +1,11 @@
{
"compilerOptions": {
"baseUrl": ".",
"moduleResolution": "node",
"target": "es6",
"jsx": "preserve",
"sourceMap": true,
"experimentalDecorators": true,
"types": [ "webpack-env", "whatwg-fetch" ],
"paths": {
// Fix "Duplicate identifier" errors caused by multiple dependencies fetching their own copies of type definitions.
// We tell TypeScript which type definitions module to treat as the canonical one (instead of combining all of them).
"history": ["./node_modules/@types/history/index"],
"redux": ["./node_modules/@types/redux/index"],
"react": ["./node_modules/@types/react/index"]
}
"types": [ "webpack-env", "whatwg-fetch" ]
},
"exclude": [
"node_modules"

View File

@@ -0,0 +1,3 @@
/node_modules/
/dist/
/bundles/

View File

@@ -0,0 +1,4 @@
/src/
/tsconfig.json
/build.js
/typings/

View File

@@ -0,0 +1,33 @@
// -------------
// No need to invoke this directly. To run a build, execute:
// npm run prepublish
// -------------
var Builder = require('systemjs-builder');
var builder = new Builder('./');
builder.config({
defaultJSExtensions: true,
paths: {
'angular2-aspnet': 'dist/Exports',
'angular2-aspnet/*': 'dist/*'
},
meta: {
'angular2/*': { build: false },
'rxjs/*': { build: false }
}
});
var entryPoint = 'dist/Exports';
var tasks = [
builder.bundle(entryPoint, './bundles/angular2-aspnet.js'),
builder.bundle(entryPoint, './bundles/angular2-aspnet.min.js', { minify: true })
];
Promise.all(tasks)
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.error('Build error');
console.error(err);
});

View File

@@ -0,0 +1,32 @@
{
"name": "angular2-aspnet",
"version": "0.0.6",
"description": "Helpers for Angular 2 apps built on ASP.NET",
"main": "./dist/Exports",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prepublish": "rimraf *.d.ts dist/*.d.ts && tsc && node build.js"
},
"typings": "dist/Exports",
"author": "Microsoft",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/aspnet/JavaScriptServices/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/aspnet/JavaScriptServices.git"
},
"dependencies": {
"angular2": "2.0.0-beta.13",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.2"
},
"devDependencies": {
"es6-shim": "^0.35.0",
"rimraf": "^2.5.4",
"systemjs-builder": "^0.14.11",
"typescript": "^2.0.0",
"zone.js": "^0.6.10"
}
}

View File

@@ -0,0 +1,59 @@
If you just want to use this package, then you *don't have to build it*. Instead, just grab the prebuilt package from NPM:
npm install angular2-aspnet
The rest of this file is notes for anyone contributing to this package itself.
## How to build
Run the following:
npm install
npm run prepublish
Requirements:
* Node, NPM
* `tsc` installed globally (via `npm install -g typescript`)
## Project structure
This package is intended to be consumable both on the server in Node.js, and on the client. Also, it's written in TypeScript,
which neither of those environments knows natively, but the TypeScript type definitions need to get delivered with the package
so that developers get a good IDE experience when consuming it.
The build process is therefore:
1. Compile the TypeScript to produce the development-time (.d.ts) and server-side (.js) artifacts
`tsc` reads `tsconfig.json` and is instructed to compile all the `.ts` files in `src/`. It produces a corresponding
structure of `.js` and `.d.ts` files in `dist/`.
When a developer consumes the resulting package (via `npm install angular2-aspnet`),
- No additional copy of `angular2` will be installed, because this package's dependency on it is declared as a
`peerDependency`. This means it will work with whatever (compatible) version of `angular2` is already installed.
- At runtime inside Node.js, the `main` configuration in `package.json` means the developer can use a standard
`import` statement to consume this package (i.e., `import * from 'angular2-aspnet';` in either JS or TS files).
- At development time inside an IDE such as Visual Studio Code, the `typings` configuration in `package.json` means
the IDE will use the corresponding `.d.ts` file as type metadata for the variable imported that way.
2. Use the SystemJS builder to produce the client-side artifacts
`build.js` uses the SystemJS Builder API to combine files in `dist/` into `.js` files ready for use in client-side
SystemJS environments, and puts them in `bundles/`. The bundle files contain `System.register` calls so that any
other part of your client-side code that tries to import `angular2-aspnet` via SystemJS will get that module at runtime.
To make it work in an application:
- Set up some build step that copies your chosen bundle file from `bundles/` to some location where it will
be served to the client
- Below your `<script>` tag that loads SystemJS itself, and above the `<script>` tag that makes the first call to
`System.import`, have a `<script>` tag that loads the desired `angular2-aspnet.js` bundle file
For an example, see https://github.com/aspnet/NodeServices/tree/master/samples/angular/MusicStore
Of course, you can also bundle the `angular2-aspnet.js` file into a larger SystemJS bundle if you want to combine
it with the rest of the code in your application.
Currently, this build system does *not* attempt to send sourcemaps of the original TypeScript to the client. This
could be added if a strong need emerges.

View File

@@ -0,0 +1,59 @@
import { provide, Injectable, Provider } from 'angular2/core';
import { Connection, ConnectionBackend, Http, XHRBackend, RequestOptions, Request, RequestMethod, Response, ResponseOptions, ReadyState } from 'angular2/http';
@Injectable()
export class CachePrimedConnectionBackend extends ConnectionBackend {
private _preCachedResponses: PreCachedResponses;
constructor(private _underlyingBackend: ConnectionBackend, private _baseResponseOptions: ResponseOptions) {
super();
this._preCachedResponses = (<any>window).__preCachedResponses || {};
}
public createConnection(request: Request): Connection {
let cacheKey = request.url;
if (request.method === RequestMethod.Get && this._preCachedResponses.hasOwnProperty(cacheKey)) {
return new CacheHitConnection(request, this._preCachedResponses[cacheKey], this._baseResponseOptions);
} else {
return this._underlyingBackend.createConnection(request);
}
}
}
class CacheHitConnection implements Connection {
readyState: ReadyState;
request: Request;
response: any;
constructor (req: Request, cachedResponse: PreCachedResponse, baseResponseOptions: ResponseOptions) {
this.request = req;
this.readyState = ReadyState.Done;
// Workaround for difficulty consuming CommonJS default exports in TypeScript. Note that it has to be a dynamic
// 'require', and not an 'import' statement, because the module isn't available on the server.
let obsCtor: any = require('rxjs/Observable').Observable;
this.response = new obsCtor(responseObserver => {
let response = new Response(new ResponseOptions({ body: cachedResponse.body, status: cachedResponse.statusCode }));
responseObserver.next(response);
responseObserver.complete();
});
}
}
declare var require: any; // Part of the workaround mentioned below. Can remove this after updating Angular.
interface PreCachedResponses {
[url: string]: PreCachedResponse;
}
interface PreCachedResponse {
statusCode: number;
body: string;
}
export const CACHE_PRIMED_HTTP_PROVIDERS = [
provide(Http, {
useFactory: (xhrBackend, requestOptions, responseOptions) => new Http(new CachePrimedConnectionBackend(xhrBackend, responseOptions), requestOptions),
deps: [XHRBackend, RequestOptions, ResponseOptions]
}),
];

View File

@@ -0,0 +1,2 @@
export * from './CachePrimedHttp';
export * from './Validation';

View File

@@ -0,0 +1,34 @@
import { ControlGroup } from 'angular2/common';
import { Response } from 'angular2/http';
export class Validation {
public static showValidationErrors(response: ValidationErrorResult | Response, controlGroup: ControlGroup): void {
if (response instanceof Response) {
var httpResponse = <Response>response;
response = <ValidationErrorResult>(httpResponse.json());
}
// It's not yet clear whether this is a legitimate and supported use of the ng.ControlGroup API.
// Need feedback from the Angular 2 team on whether there's a better way.
var errors = <ValidationErrorResult>response;
Object.keys(errors || {}).forEach(key => {
errors[key].forEach(errorMessage => {
// If there's a specific control for this key, then use it. Otherwise associate the error
// with the whole control group.
var control = controlGroup.controls[key] || controlGroup;
// This is rough. Need to find out if there's a better way, or if this is even supported.
if (!control.errors) {
(<any>control)._errors = {};
}
control.errors[errorMessage] = true;
});
});
}
}
export interface ValidationErrorResult {
[propertyName: string]: string[];
}

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": false,
"declaration": true,
"experimentalDecorators": true,
"noLib": false,
"outDir": "./dist",
"lib": ["es2015", "dom"]
},
"exclude": [
"node_modules"
]
}

View File

@@ -1,6 +1,6 @@
{
"description": "Helpers for building Angular 2 applications on ASP.NET Core.",
"version": "1.1.0-beta2-*",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",

View File

@@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]
[assembly: AssemblyCompany("Microsoft Corporation.")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]

View File

@@ -1,6 +1,6 @@
{
"description": "Socket-based RPC for Microsoft.AspNetCore.NodeServices",
"version": "1.1.0-beta2-*",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",

View File

@@ -125,7 +125,7 @@
// Signal to the NodeServices base class that we're ready to accept invocations
console.log('[Microsoft.AspNetCore.NodeServices:Listening]');
});
ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true);
ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid));
function readRequestBodyAsJson(request, callback) {
var requestBodyAsString = '';
request.on('data', function (chunk) { requestBodyAsString += chunk; });
@@ -255,7 +255,7 @@
*/
"use strict";
var pollIntervalMs = 1000;
function exitWhenParentExits(parentPid, ignoreSigint) {
function exitWhenParentExits(parentPid) {
setInterval(function () {
if (!processExists(parentPid)) {
// Can't log anything at this point, because out stdout was connected to the parent,
@@ -263,24 +263,6 @@
process.exit();
}
}, pollIntervalMs);
if (ignoreSigint) {
// Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree.
// By default, the Node process would then exit before the .NET process, because ASP.NET implements
// a delayed shutdown to allow ongoing requests to complete.
//
// This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware
// will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is
// already set up to shut itself down if it detects the .NET process is terminated, all we have to do is
// ignore the SIGINT. The Node process will then terminate automatically after the .NET process does.
//
// A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any
// ongoing EventSource connections before letting the Node process exit, independently of the .NET
// process exiting. However, doing this well in general is very nontrivial (see all the discussion at
// https://github.com/nodejs/node/issues/2642).
process.on('SIGINT', function () {
console.log('Received SIGINT. Waiting for .NET process to exit...');
});
}
}
exports.exitWhenParentExits = exitWhenParentExits;
function processExists(pid) {

View File

@@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
case "application/octet-stream":
// Streamed responses have to be received as System.IO.Stream instances
if (typeof(T) != typeof(Stream) && typeof(T) != typeof(object))
if (typeof(T) != typeof(Stream))
{
throw new ArgumentException(
"Node module responded with binary stream. This cannot be converted to the requested generic type: " +
@@ -136,4 +136,4 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
}
}
}
}
}

View File

@@ -222,7 +222,7 @@ If you haven't yet installed node-inspector, you can do so as follows:
}
else
{
debuggingArgs = string.Empty;
debuggingArgs = string.Empty;
}
var thisProcessPid = Process.GetCurrentProcess().Id;
@@ -325,26 +325,17 @@ If you haven't yet installed node-inspector, you can do so as follows:
private static Process LaunchNodeProcess(ProcessStartInfo startInfo)
{
try {
var process = Process.Start(startInfo);
var process = Process.Start(startInfo);
// On Mac at least, a killed child process is left open as a zombie until the parent
// captures its exit code. We don't need the exit code for this process, and don't want
// to use process.WaitForExit() explicitly (we'd have to block the thread until it really
// has exited), but we don't want to leave zombies lying around either. It's sufficient
// to use process.EnableRaisingEvents so that .NET will grab the exit code and let the
// zombie be cleaned away without having to block our thread.
process.EnableRaisingEvents = true;
// On Mac at least, a killed child process is left open as a zombie until the parent
// captures its exit code. We don't need the exit code for this process, and don't want
// to use process.WaitForExit() explicitly (we'd have to block the thread until it really
// has exited), but we don't want to leave zombies lying around either. It's sufficient
// to use process.EnableRaisingEvents so that .NET will grab the exit code and let the
// zombie be cleaned away without having to block our thread.
process.EnableRaisingEvents = true;
return process;
} catch (Exception ex) {
var message = "Failed to start Node process. To resolve this:.\n\n"
+ "[1] Ensure that Node.js is installed and can be found in one of the PATH directories.\n"
+ $" Current PATH enviroment variable is: { Environment.GetEnvironmentVariable("PATH") }\n"
+ " Make sure the Node executable is in one of those directories, or update your PATH.\n\n"
+ "[2] See the InnerException for further details of the cause.";
throw new InvalidOperationException(message, ex);
}
return process;
}
private static string UnencodeNewlines(string str)

View File

@@ -67,7 +67,7 @@ public async Task<IActionResult> MyAction([FromServices] INodeServices nodeServi
}
```
Of course, you also need to supply the Node.js code you want to invoke. Create a file called `addNumbers.js` at the root of your ASP.NET Core application, and add the following code:
Of course, you also need to supply the Node.js code you want to invoke. Create a file called `addNumber.js` at the root of your ASP.NET Core application, and add the following code:
```javascript
module.exports = function (callback, first, second) {
@@ -333,23 +333,25 @@ People have asked about using [VroomJS](https://github.com/fogzot/vroomjs) as a
### Built-in hosting models
Normally, you can just use the default hosting model, and not worry about it. But if you have some special requirements, you can write your own hosting model, or reference a package that supplies one.
For example, you could use the 'socket' hosting model. It performs RPC between .NET and Node.js using a fast, low-level binary channel rather than the default HTTP transport. To do this, first install the NuGet package `Microsoft.AspNetCore.NodeServices.Sockets`. Then, at the top of your `Startup.cs` file, add:
Normally, you can just use the default hosting model, and not worry about it. But if you have some special requirements, select a hosting model by setting the `HostingModel` property on the `options` object in `AddNodeServices`. Example:
```csharp
using Microsoft.AspNetCore.NodeServices.Sockets;
```
...then in your `Startup.cs` file's `ConfigureServices` method, you can configure:
```csharp
services.AddNodeServices(options => {
options.UseSocketHosting();
services.AddNodeServices(options =>
{
options.HostingModel = NodeHostingModel.Socket;
});
```
Now when you run your application, it will use the socket-based hosting and transport mechanism. In the past, the socket transport was faster than HTTP, but since .NET Core 1.1 improved the performance of `HttpClient` there isn't really any speed difference any more, so there's no longer any significant advantage to using `Microsoft.AspNetCore.NodeServices.Sockets`.
**Available hosting models**
* `Socket`
* Launches Node as a separate process, and communicates with it using named pipes (on Windows) or domain sockets (on Linux / OS X).
* This is faster than `Http` because it uses a low-level binary protocol with very low overhead. It retains one continuous connection for the whole lifetime of the Node instance, so it doesn't have to keep waiting for new connections to open.
* `Http` (default)
* Launches Node as a separate process, and communicates with it by making HTTP requests.
* This primarily exists because it was implemented before `Socket`, but there's no particular reason to use it now that `Socket` is available. It could theoretically be useful if you wanted to run Node instances on separate servers (though there isn't currently any built-in API for configuring that).
The default transport may change from `Http` to `Socket` in the near future, because it's faster.
### Custom hosting models

View File

@@ -76,7 +76,7 @@ server.listen(requestedPortOrZero, 'localhost', function () {
console.log('[Microsoft.AspNetCore.NodeServices:Listening]');
});
exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true);
exitWhenParentExits(parseInt(parsedArgs.parentPid));
function readRequestBodyAsJson(request, callback) {
let requestBodyAsString = '';

View File

@@ -34,7 +34,7 @@ to check whether the parent PID is still running. So that's what we do here.
const pollIntervalMs = 1000;
export function exitWhenParentExits(parentPid: number, ignoreSigint: boolean) {
export function exitWhenParentExits(parentPid: number) {
setInterval(() => {
if (!processExists(parentPid)) {
// Can't log anything at this point, because out stdout was connected to the parent,
@@ -42,25 +42,6 @@ export function exitWhenParentExits(parentPid: number, ignoreSigint: boolean) {
process.exit();
}
}, pollIntervalMs);
if (ignoreSigint) {
// Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree.
// By default, the Node process would then exit before the .NET process, because ASP.NET implements
// a delayed shutdown to allow ongoing requests to complete.
//
// This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware
// will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is
// already set up to shut itself down if it detects the .NET process is terminated, all we have to do is
// ignore the SIGINT. The Node process will then terminate automatically after the .NET process does.
//
// A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any
// ongoing EventSource connections before letting the Node process exit, independently of the .NET
// process exiting. However, doing this well in general is very nontrivial (see all the discussion at
// https://github.com/nodejs/node/issues/2642).
process.on('SIGINT', () => {
console.log('Received SIGINT. Waiting for .NET process to exit...');
});
}
}
function processExists(pid: number) {
@@ -75,7 +56,7 @@ function processExists(pid: number) {
if (ex.code === 'EPERM') {
throw new Error(`Attempted to check whether process ${pid} was running, but got a permissions error.`);
}
return false;
}
}

View File

@@ -1,6 +1,6 @@
{
"description": "Invoke Node.js modules at runtime in ASP.NET Core applications.",
"version": "1.1.0-rc1-*",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",

View File

@@ -0,0 +1,3 @@
/node_modules/
/*.js
/*.d.ts

View File

@@ -0,0 +1,3 @@
!/*.js
!/*.d.ts
/typings/

View File

@@ -0,0 +1,12 @@
Copyright (c) .NET Foundation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

View File

@@ -0,0 +1 @@
TODO

View File

@@ -0,0 +1,27 @@
{
"name": "redux-typed",
"version": "2.0.0",
"description": "Helpers for building React+Redux apps with strong TypeScript type checking everywhere",
"main": "main.js",
"typings": "main.d.ts",
"scripts": {
"prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"redux-typed\"'",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Microsoft",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/aspnet/JavaScriptServices/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/aspnet/JavaScriptServices.git"
},
"devDependencies": {
"@types/react": "^0.14.38",
"@types/react-redux": "^4.4.32",
"@types/redux": "^3.5.30",
"rimraf": "^2.5.4",
"typescript": "^2.0.0"
}
}

View File

@@ -0,0 +1,24 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
if (typeof (<any>Object).assign != 'function') {
(function () {
(<any>Object).assign = function (target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}

View File

@@ -0,0 +1,42 @@
// Credits for the type detection technique: http://www.bluewire-technologies.com/2015/redux-actions-for-typescript/
import * as React from 'react';
import { Dispatch } from 'redux';
import './ObjectAssignPolyfill';
export interface ActionClass<T extends Action> {
prototype: T;
}
export function typeName(name: string) {
return function<T extends Action>(actionClass: ActionClass<T>) {
// Although we could determine the type name using actionClass.prototype.constructor.name,
// it's dangerous to do that because minifiers may interfere with it, and then serialized state
// might not have the expected meaning after a recompile. So we explicitly ask for a name string.
actionClass.prototype.type = name;
}
}
export function isActionType<T extends Action>(action: Action, actionClass: ActionClass<T>): action is T {
return action.type == actionClass.prototype.type;
}
// Middleware for transforming Typed Actions into plain actions
export const typedToPlain = (store: any) => (next: any) => (action: any) => {
next((<any>Object).assign({}, action));
};
export abstract class Action {
type: string;
constructor() {
// Make it an own-property (not a prototype property) so that it's included when JSON-serializing
this.type = this.type;
}
}
export interface Reducer<TState> extends Function {
(state: TState, action: Action): TState;
}
export interface ActionCreatorGeneric<TState> extends Function {
(dispatch: Dispatch<TState>, getState: () => TState): any;
}

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
import { connect as nativeConnect } from 'react-redux';
export type ReactComponentClass<T, S> = new(props: T) => React.Component<T, S>;
export class ComponentBuilder<TOwnProps, TActions, TExternalProps> {
constructor(private stateToProps: (appState: any) => TOwnProps, private actionCreators: TActions) {
}
public withExternalProps<TAddExternalProps>() {
return this as any as ComponentBuilder<TOwnProps, TActions, TAddExternalProps>;
}
public get allProps(): TOwnProps & TActions & TExternalProps { return null; }
public connect<TState>(componentClass: ReactComponentClass<TOwnProps & TActions & TExternalProps, TState>): ReactComponentClass<TExternalProps, TState> {
return nativeConnect(this.stateToProps, this.actionCreators as any)(componentClass) as any;
}
}
export function provide<TOwnProps, TActions>(stateToProps: (appState: any) => TOwnProps, actionCreators: TActions) {
return new ComponentBuilder<TOwnProps, TActions, {}>(stateToProps, actionCreators);
}

View File

@@ -0,0 +1,2 @@
export * from './StrongActions';
export * from './StrongProvide';

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"moduleResolution": "node",
"module": "commonjs",
"target": "es5",
"declaration": true,
"outDir": ".",
"lib": ["es2015", "dom"]
},
"files": [
"src/main.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,6 +1,6 @@
{
"description": "Helpers for building React applications on ASP.NET Core.",
"version": "1.1.0-beta2-*",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",

View File

@@ -538,7 +538,7 @@ module.exports = {
output: {
path: path.join(__dirname, 'wwwroot', 'dist'),
publicPath: '/dist/',
publicPath: '/dist',
filename: '[name].js'
},
};

View File

@@ -1,6 +1,6 @@
{
"name": "aspnet-webpack",
"version": "1.0.26",
"version": "1.0.25",
"description": "Helpers for using Webpack in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
"main": "index.js",
"scripts": {

View File

@@ -108,7 +108,7 @@ function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configurati
// file on disk wouldn't match the file served to the browser, and the source map line numbers wouldn't
// match up. Breakpoints would either not be hit, or would hit the wrong lines.
(compiler as any).plugin('done', stats => {
copyRecursiveToRealFsSync(compiler.outputFileSystem, '/', [/\.hot-update\.(js|json|js\.map)$/]);
copyRecursiveToRealFsSync(compiler.outputFileSystem, '/', [/\.hot-update\.(js|json)$/]);
});
if (enableHotModuleReplacement) {

View File

@@ -1,6 +1,6 @@
{
"description": "Helpers for building single-page applications on ASP.NET MVC Core",
"version": "1.1.0-rc1-*",
"version": "1.1.0-*",
"packOptions": {
"repository": {
"type": "git",

View File

@@ -1,2 +0,0 @@
[config]
SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json

View File

@@ -1,90 +0,0 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<GlobalExclude>$(GlobalExclude);bin\**;obj\**;node_modules\**;**\*.user;**\*.suo;**\*.*proj;.git\**</GlobalExclude>
</PropertyGroup>
<ItemGroup>
<None Include="**\*" Exclude="$(GlobalExclude)"/>
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Web">
<Version>1.0.0-alpha-20161104-2-112</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.SpaServices">
<Version>1.1.0-*</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
<Version>1.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove=".bowerrc" />
<None Remove="yarn.lock" />
<!-- Files not to publish (note that the 'dist' subfolders are re-added below) -->
<Content Remove="ClientApp\**" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View File

@@ -5,7 +5,7 @@ import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
import { CounterComponent } from 'ClientApp/app/components/counter/counter.component';
@NgModule({
bootstrap: [ AppComponent ],

View File

@@ -1,4 +1,4 @@
<h1>Counter</h1>
<h2>Counter</h2>
<p>This is a simple example of an Angular 2 component.</p>

View File

@@ -13,7 +13,7 @@ describe('Counter component', () => {
});
it('should display a title', async(() => {
const titleText = fixture.nativeElement.querySelector('h1').textContent;
const titleText = fixture.nativeElement.querySelector('h2').textContent;
expect(titleText).toEqual('Counter');
}));

View File

@@ -18,7 +18,6 @@ module.exports = function (config) {
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
mime: { 'application/javascript': ['ts','tsx'] },
singleRun: false,
webpack: require('../../webpack.config.js').filter(config => config.target !== 'node'), // Test against client bundle, because tests run in a browser
webpackMiddleware: { stats: 'errors-only' }

View File

@@ -27,7 +27,7 @@
"es6-shim": "^0.35.1",
"event-source-polyfill": "^0.0.7",
"expose-loader": "^0.7.1",
"extract-text-webpack-plugin": "^1.0.1",
"extract-text-webpack-plugin": "^2.0.0-beta",
"file-loader": "^0.9.0",
"html-loader": "^0.4.4",
"isomorphic-fetch": "^2.2.1",
@@ -39,10 +39,10 @@
"rxjs": "5.0.0-beta.12",
"style-loader": "^0.13.1",
"to-string-loader": "^1.1.5",
"ts-loader": "^0.8.2",
"typescript": "^2.0.3",
"ts-loader": "^1.3.0",
"typescript": "^2.1.0",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack": "^2.1.0-beta",
"webpack-hot-middleware": "^2.12.2",
"webpack-merge": "^0.14.1",
"zone.js": "^0.6.25"

View File

@@ -0,0 +1,11 @@
------------------------------------------------------------------
Don't delete this file. Do include it in your source control repo.
------------------------------------------------------------------
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -1,5 +1,11 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"*"
]
},
"moduleResolution": "node",
"target": "es5",
"sourceMap": true,

View File

@@ -6,7 +6,13 @@ var merge = require('webpack-merge');
// Configuration in common to both client-side and server-side bundles
var sharedConfig = {
context: __dirname,
resolve: { extensions: [ '', '.js', '.ts' ] },
resolve: {
extensions: [ '.js', '.ts' ],
modules: [
'node_modules',
'.'
]
},
output: {
filename: '[name].js',
publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
@@ -16,8 +22,7 @@ var sharedConfig = {
{ test: /\.ts$/, include: /ClientApp/, loaders: ['ts-loader?silent=true', 'angular2-template-loader'] },
{ test: /\.html$/, loader: 'html-loader?minimize=false' },
{ test: /\.css$/, loader: 'to-string-loader!css-loader' },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
{ test: /\.json$/, loader: 'json-loader' }
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } }
]
}
};
@@ -47,7 +52,7 @@ var clientBundleConfig = merge(sharedConfig, {
// Configuration for server-side (prerendering) bundle suitable for running in Node
var serverBundleConfig = merge(sharedConfig, {
resolve: { packageMains: ['main'] },
resolve: { mainFields: ['main'] },
entry: { 'main-server': './ClientApp/boot-server.ts' },
plugins: [
new webpack.DllReferencePlugin({

View File

@@ -6,7 +6,7 @@ var merge = require('webpack-merge');
var extractCSS = new ExtractTextPlugin('vendor.css');
var sharedConfig = {
resolve: { extensions: [ '', '.js' ] },
resolve: { extensions: [ '.js' ] },
module: {
loaders: [
{ test: /\.json$/, loader: require.resolve('json-loader') },
@@ -68,7 +68,7 @@ var clientBundleConfig = merge(sharedConfig, {
var serverBundleConfig = merge(sharedConfig, {
target: 'node',
resolve: { packageMains: ['main'] },
resolve: { mainFields: ['main'] },
output: {
path: path.join(__dirname, 'ClientApp', 'dist'),
libraryTarget: 'commonjs2',

View File

@@ -1,2 +0,0 @@
[config]
SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json

View File

@@ -1,87 +0,0 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<GlobalExclude>$(GlobalExclude);bin\**;obj\**;node_modules\**;**\*.user;**\*.suo;**\*.*proj;.git\**</GlobalExclude>
</PropertyGroup>
<ItemGroup>
<None Include="**\*" Exclude="$(GlobalExclude)"/>
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Web">
<Version>1.0.0-alpha-20161104-2-112</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.SpaServices">
<Version>1.1.0-*</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
<Version>1.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove=".bowerrc" />
<None Remove="yarn.lock" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View File

@@ -1,5 +1,5 @@
<template>
<h1>Counter</h1>
<h2>Counter</h2>
<p>This is a simple example of an Aurelia component.</p>

View File

@@ -33,7 +33,6 @@
"file-loader": "^0.9.0",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.22.0",
"json-loader": "^0.5.4",
"raw-loader": "^0.5.1",
"style-loader": "^0.13.1",
"to-string-loader": "^1.1.5",

View File

@@ -0,0 +1,11 @@
------------------------------------------------------------------
Don't delete this file. Do include it in your source control repo.
------------------------------------------------------------------
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -17,8 +17,7 @@ module.exports = {
{ test: /\.ts$/, include: /ClientApp/, loader: 'ts-loader', query: { silent: true } },
{ test: /\.html$/, loader: 'html-loader' },
{ test: /\.css$/, loaders: [ 'style-loader', 'css-loader' ] },
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' },
{ test: /\.json$/, loader: 'json-loader' }
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' }
]
},
plugins: [

View File

@@ -1,2 +0,0 @@
[config]
SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json

View File

@@ -1,4 +1,4 @@
<h1>Counter</h1>
<h2>Counter</h2>
<p>This is a simple example of a Knockout component.</p>

View File

@@ -1,87 +0,0 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<GlobalExclude>$(GlobalExclude);bin\**;obj\**;node_modules\**;**\*.user;**\*.suo;**\*.*proj;.git\**</GlobalExclude>
</PropertyGroup>
<ItemGroup>
<None Include="**\*" Exclude="$(GlobalExclude)"/>
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Web">
<Version>1.0.0-alpha-20161104-2-112</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.SpaServices">
<Version>1.1.0-*</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
<Version>1.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove=".bowerrc" />
<None Remove="yarn.lock" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View File

@@ -23,7 +23,6 @@
"history": "^4.3.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^2.2.1",
"json-loader": "^0.5.4",
"knockout": "^3.4.0",
"raw-loader": "^0.5.1",
"style-loader": "^0.13.1",

View File

@@ -0,0 +1,7 @@
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -17,8 +17,7 @@ module.exports = {
{ test: /\.ts$/, include: /ClientApp/, loader: 'ts-loader', query: { silent: true } },
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: isDevBuild ? 'style-loader!css-loader' : ExtractTextPlugin.extract(['css-loader']) },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
{ test: /\.json$/, loader: 'json-loader' }
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } }
]
},
plugins: [

View File

@@ -1,2 +0,0 @@
[config]
SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json

View File

@@ -8,15 +8,6 @@ import { syncHistoryWithStore } from 'react-router-redux';
import routes from './routes';
import configureStore from './configureStore';
import { ApplicationState } from './store';
import cookie from 'react-cookie';
// If the server supplied any edits to cookies, apply them on the client
const cookieDataFromServer = window['cookieData'];
if (cookieDataFromServer) {
Object.getOwnPropertyNames(cookieDataFromServer).forEach(name => {
cookie.save(name, cookieDataFromServer[name]);
});
}
// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = (window as any).initialReduxState as ApplicationState;

View File

@@ -6,28 +6,9 @@ import createMemoryHistory from 'history/lib/createMemoryHistory';
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
import routes from './routes';
import configureStore from './configureStore';
import cookieUtil from 'cookie';
import cookie from 'react-cookie';
function plugInCookiesFromDotNet(cookieData: { key: string, value: string }[], res) {
const formattedData = {};
cookieData.forEach(keyValuePair => {
formattedData[keyValuePair.key] = keyValuePair.value;
});
cookie.plugToRequest({ cookies: formattedData }, res);
}
export default createServerRenderer(params => {
return new Promise<RenderResult>((resolve, reject) => {
const cookiesModifiedOnServer = {};
if (params.data.cookies) {
// If we received some cookie data, use that to prepopulate 'react-cookie'
plugInCookiesFromDotNet(params.data.cookies, {
// Also track any cookies written on the server
cookie: (name, val) => { cookiesModifiedOnServer[name] = val; }
})
}
// Match the incoming request against the list of client-side routes
match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => {
if (error) {
@@ -61,17 +42,7 @@ export default createServerRenderer(params => {
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: {
initialReduxState: store.getState(),
// Send any cookies written during server-side prerendering to the client.
// WARNING: Do not pass any security-sensitive cookies this way, because they will become
// readable in the HTML source. If your goal is to use this approach to manage authentication
// cookies, then be sure *not* to use 'globals' to send them to the client - instead, invoke
// Microsoft.AspNetCore.SpaServices.Prerendering.Prerender directly from your .NET code and
// only send the 'html' part back to the client (or at least, not all of the 'globals').
cookieData: cookiesModifiedOnServer
}
globals: { initialReduxState: store.getState() }
});
}, reject); // Also propagate any errors back into the host application
});

View File

@@ -1,5 +1,4 @@
import { Action, Reducer, ThunkAction } from 'redux';
import cookie from 'react-cookie';
// -----------------
// STATE - This defines the type of data maintained in the Redux store.
@@ -31,20 +30,13 @@ export const actionCreators = {
// ----------------
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
const cookieKey = 'counterValue';
function modifiedCount(state: CounterState, delta: number): CounterState {
const newCount = state.count + delta;
cookie.save(cookieKey, newCount); // Ideally, don't do this here: have something that watches the store instead of having a side-effect from a reducer
return { count: newCount };
}
export const reducer: Reducer<CounterState> = (state: CounterState, action: KnownAction) => {
switch (action.type) {
case 'INCREMENT_COUNT':
return modifiedCount(state, +1);
return { count: state.count + 1 };
case 'DECREMENT_COUNT':
return modifiedCount(state, -1);
return { count: state.count - 1 };
default:
// The following line guarantees that every action in the KnownAction union has been covered by a case above
const exhaustiveCheck: never = action;
@@ -52,6 +44,5 @@ export const reducer: Reducer<CounterState> = (state: CounterState, action: Know
// For unrecognized actions (or in cases where actions have no effect), must return the existing state
// (or default initial state if none was supplied)
const prevCountFromCookie = parseInt(cookie.load(cookieKey) || '0');
return state || { count: prevCountFromCookie };
return state || { count: 0 };
};

View File

@@ -1,90 +0,0 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<GlobalExclude>$(GlobalExclude);bin\**;obj\**;node_modules\**;**\*.user;**\*.suo;**\*.*proj;.git\**</GlobalExclude>
</PropertyGroup>
<ItemGroup>
<None Include="**\*" Exclude="$(GlobalExclude)"/>
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Web">
<Version>1.0.0-alpha-20161104-2-112</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.SpaServices">
<Version>1.1.0-*</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
<Version>1.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove=".bowerrc" />
<None Remove="yarn.lock" />
<!-- Files not to publish (note that the 'dist' subfolders are re-added below) -->
<Content Remove="ClientApp\**" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View File

@@ -2,8 +2,7 @@
ViewData["Title"] = "Home Page";
}
<div id="react-app" asp-prerender-module="ClientApp/dist/main-server"
asp-prerender-data="new { cookies = ViewContext.HttpContext.Request.Cookies }">Loading...</div>
<div id="react-app" asp-prerender-module="ClientApp/dist/main-server">Loading...</div>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>

View File

@@ -2,7 +2,6 @@
"name": "WebApplicationBasic",
"version": "0.0.0",
"dependencies": {
"@types/history": "^2.0.0",
"@types/react": "^0.14.29",
"@types/react-dom": "^0.14.14",
"@types/react-redux": "^4.4.29",
@@ -32,7 +31,6 @@
"json-loader": "^0.5.4",
"node-noop": "^1.0.0",
"react": "^15.3.2",
"react-cookie": "^1.0.4",
"react-dom": "^15.3.2",
"react-redux": "^4.4.5",
"react-router": "^2.8.1",
@@ -41,7 +39,7 @@
"redux-thunk": "^2.1.0",
"style-loader": "^0.13.0",
"ts-loader": "^0.8.1",
"typescript": "^2.0.3",
"typescript": "2.0.3",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-hot-middleware": "^2.12.2",

View File

@@ -0,0 +1,7 @@
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -10,11 +10,8 @@
"lib": ["es6", "dom"],
"types": [ "webpack-env", "whatwg-fetch" ],
"paths": {
// Fix "Duplicate identifier" errors caused by multiple dependencies fetching their own copies of type definitions.
// We tell TypeScript which type definitions module to treat as the canonical one (instead of combining all of them).
"history": ["./node_modules/@types/history/index"],
"redux": ["./node_modules/@types/redux/index"],
"react": ["./node_modules/@types/react/index"]
// Fixes "Duplicate identifier 'Redux'" error when packages were installed via Yarn (by specifying which .d.ts file is authoritative)
"redux": ["./node_modules/@types/redux/index"]
}
},
"exclude": [

View File

@@ -14,8 +14,7 @@ var sharedConfig = () => ({
module: {
loaders: [
{ test: /\.tsx?$/, include: /ClientApp/, loader: 'babel-loader' },
{ test: /\.tsx?$/, include: /ClientApp/, loader: 'ts-loader', query: { silent: true } },
{ test: /\.json$/, loader: 'json-loader' }
{ test: /\.tsx?$/, include: /ClientApp/, loader: 'ts-loader', query: { silent: true } }
]
}
});

View File

@@ -1,2 +0,0 @@
[config]
SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json

View File

@@ -1,87 +0,0 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<GlobalExclude>$(GlobalExclude);bin\**;obj\**;node_modules\**;**\*.user;**\*.suo;**\*.*proj;.git\**</GlobalExclude>
</PropertyGroup>
<ItemGroup>
<None Include="**\*" Exclude="$(GlobalExclude)"/>
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Web">
<Version>1.0.0-alpha-20161104-2-112</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.SpaServices">
<Version>1.1.0-*</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
<Version>1.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove=".bowerrc" />
<None Remove="yarn.lock" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View File

@@ -20,7 +20,6 @@
"file-loader": "^0.9.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^2.2.1",
"json-loader": "^0.5.4",
"react": "^15.3.2",
"react-dom": "^15.3.2",
"react-router": "^2.8.1",

View File

@@ -0,0 +1,7 @@
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -1,17 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"moduleResolution": "node",
"target": "es6",
"jsx": "preserve",
"sourceMap": true,
"skipDefaultLibCheck": true,
"paths": {
// Fix "Duplicate identifier" errors caused by multiple dependencies fetching their own copies of type definitions.
// We tell TypeScript which type definitions module to treat as the canonical one (instead of combining all of them).
"history": ["./node_modules/@types/history/index"],
"react": ["./node_modules/@types/react/index"]
}
"skipDefaultLibCheck": true
},
"exclude": [
"bin",

View File

@@ -18,8 +18,7 @@ module.exports = {
{ test: /\.ts(x?)$/, include: /ClientApp/, loader: 'babel-loader' },
{ test: /\.tsx?$/, include: /ClientApp/, loader: 'ts-loader', query: { silent: true } },
{ test: /\.css$/, loader: isDevBuild ? 'style-loader!css-loader' : ExtractTextPlugin.extract(['css-loader']) },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
{ test: /\.json$/, loader: 'json-loader' }
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } }
]
},
plugins: [

View File

@@ -0,0 +1,7 @@
This file exists as a workaround for https://github.com/dotnet/cli/issues/1396
('dotnet publish' does not publish any directories that didn't exist or were
empty before the publish script started, which means it's not enough just to
run 'npm install' during publishing: you need to ensure node_modules already
existed at least).
Hopefully, this can be removed after the move to the new MSBuild.

View File

@@ -10,7 +10,6 @@
"author": "Microsoft",
"license": "Apache-2.0",
"dependencies": {
"@types/chalk": "^0.4.31",
"@types/semver": "^5.3.30",
"diff": "^2.2.2",
"gitignore-parser": "0.0.2",
@@ -18,8 +17,7 @@
"lodash": "^4.11.1",
"mkdirp": "^0.5.1",
"rimraf": "^2.5.2",
"semver": "^5.3.0",
"tar.gz": "^1.0.5"
"semver": "^5.3.0"
},
"devDependencies": {
"@types/glob": "^5.0.30",

View File

@@ -6,23 +6,13 @@ import * as _ from 'lodash';
import * as mkdirp from 'mkdirp';
import * as rimraf from 'rimraf';
import * as childProcess from 'child_process';
import * as targz from 'tar.gz';
const isWindows = /^win/.test(process.platform);
const textFileExtensions = ['.gitignore', 'template_gitignore', '.config', '.cs', '.cshtml', '.csproj', 'Dockerfile', '.html', '.js', '.json', '.jsx', '.md', '.nuspec', '.ts', '.tsx', '.xproj'];
const textFileExtensions = ['.gitignore', 'template_gitignore', '.config', '.cs', '.cshtml', 'Dockerfile', '.html', '.js', '.json', '.jsx', '.md', '.nuspec', '.ts', '.tsx', '.xproj'];
const yeomanGeneratorSource = './src/yeoman';
// For the Angular 2 template, we want to bundle prebuilt dist dev-mode files, because the VS template can't auto-run
// webpack on project creation. Note that these script entries are *not* the same as the project's usual prepublish
// scripts, because here we want dev-mode builds (e.g., to support HMR), not prod-mode builds.
const runWebpackInDevModeScripts = [
'npm install',
'node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js',
'node node_modules/webpack/bin/webpack.js'
];
const templates: { [key: string]: { dir: string, dotNetNewId: string, displayName: string, prepublish?: string[], forceInclusion?: RegExp } } = {
'angular-2': { dir: '../../templates/Angular2Spa/', dotNetNewId: 'Angular', displayName: 'Angular 2', prepublish: runWebpackInDevModeScripts, forceInclusion: /^(wwwroot|ClientApp)\/dist\// },
const templates: { [key: string]: { dir: string, dotNetNewId: string, displayName: string, forceInclusion?: RegExp } } = {
'angular-2': { dir: '../../templates/Angular2Spa/', dotNetNewId: 'Angular', displayName: 'Angular 2', forceInclusion: /^(wwwroot|ClientApp)\/dist\// },
'aurelia': { dir: '../../templates/AureliaSpa/', dotNetNewId: 'Aurelia', displayName: 'Aurelia' },
'knockout': { dir: '../../templates/KnockoutSpa/', dotNetNewId: 'Knockout', displayName: 'Knockout.js' },
'react-redux': { dir: '../../templates/ReactReduxSpa/', dotNetNewId: 'ReactRedux', displayName: 'React.js and Redux' },
@@ -82,25 +72,21 @@ function copyRecursive(sourceRoot: string, destRoot: string, matchGlob: string)
});
}
function buildYeomanNpmPackage(outputRoot: string) {
function buildYeomanNpmPackage() {
const outputRoot = './dist/generator-aspnetcore-spa';
const outputTemplatesRoot = path.join(outputRoot, 'app/templates');
rimraf.sync(outputTemplatesRoot);
// Copy template files
const filenameReplacements = [
{ from: /.*\.xproj$/, to: 'tokenreplace-namePascalCase.xproj' },
{ from: /.*\.csproj$/, to: 'tokenreplace-namePascalCase.csproj' }
{ from: /.*\.xproj$/, to: 'tokenreplace-namePascalCase.xproj' }
];
const contentReplacements = [
// .xproj items
{ from: /\bWebApplicationBasic\b/g, to: '<%= namePascalCase %>' },
{ from: /<ProjectGuid>[0-9a-f\-]{36}<\/ProjectGuid>/g, to: '<ProjectGuid><%= projectGuid %></ProjectGuid>' },
{ from: /<RootNamespace>.*?<\/RootNamespace>/g, to: '<RootNamespace><%= namePascalCase %></RootNamespace>'},
{ from: /\s*<BaseIntermediateOutputPath.*?<\/BaseIntermediateOutputPath>/g, to: '' },
{ from: /\s*<OutputPath.*?<\/OutputPath>/g, to: '' },
// global.json items
{ from: /1\.0\.0-preview2-1-003177/, to: '<%= sdkVersion %>' }
];
_.forEach(templates, (templateConfig, templateName) => {
const outputDir = path.join(outputTemplatesRoot, templateName);
@@ -124,9 +110,12 @@ function buildDotNetNewNuGetPackage() {
const sourceProjectName = 'WebApplicationBasic';
const projectGuid = '00000000-0000-0000-0000-000000000000';
const filenameReplacements = [
// TODO: For dotnetnew templates, switch to csproj. No need for SDK choice as it can be Preview3+ only.
{ from: /.*\.xproj$/, to: `${sourceProjectName}.xproj` },
{ from: /\btemplate_gitignore$/, to: '.gitignore' }
{ from: /\btemplate_gitignore$/, to: '.gitignore' },
// Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
// For details, see the comment in ../yeoman/app/index.ts
{ from: /\btemplate_nodemodules_placeholder.txt$/, to: 'node_modules/_placeholder.txt' }
];
const contentReplacements = [
{ from: /<ProjectGuid>[0-9a-f\-]{36}<\/ProjectGuid>/g, to: `<ProjectGuid>${projectGuid}</ProjectGuid>` },
@@ -172,37 +161,21 @@ function buildDotNetNewNuGetPackage() {
rimraf.sync('./tmp');
}
function runAllPrepublishScripts() {
Object.getOwnPropertyNames(templates).forEach(templateKey => {
const templateInfo = templates[templateKey];
if (templateInfo.prepublish) {
runScripts(templateInfo.dir, templateInfo.prepublish);
}
});
// TODO: Instead of just showing this warning, improve build script so it actually does build them
// in the correct format. Can do this once we've moved away from using ASPNETCORE_ENVIRONMENT to
// control the build output mode. The templates we warn about here are the ones where we ship some
// files that wouldn't normally be under source control (e.g., /wwwroot/dist/*).
const templatesWithForceIncludes = Object.getOwnPropertyNames(templates)
.filter(templateName => !!templates[templateName].forceInclusion);
if (templatesWithForceIncludes.length > 0) {
console.warn(`
---
WARNING: Ensure that the following templates are already built in the configuration desired for publishing.
For example, build the dist files in debug mode.
TEMPLATES: ${templatesWithForceIncludes.join(', ')}
---
`);
}
function runScripts(rootDir: string, scripts: string[]) {
console.log(`[Prepublish] In directory: ${ rootDir }`);
scripts.forEach(script => {
console.log(`[Prepublish] Running: ${ script }`);
childProcess.execSync(script, { cwd: rootDir, stdio: 'inherit' });
});
console.log(`[Prepublish] Done`)
}
const distDir = './dist';
const artifactsDir = path.join(distDir, 'artifacts');
const yeomanOutputRoot = path.join(distDir, 'generator-aspnetcore-spa');
rimraf.sync(distDir);
mkdirp.sync(artifactsDir);
runAllPrepublishScripts();
buildYeomanNpmPackage(yeomanOutputRoot);
buildYeomanNpmPackage();
buildDotNetNewNuGetPackage();
// Finally, create a .tar.gz file containing the built generator-aspnetcore-spa.
// The CI system can treat this as the final built artifact.
// Note that the targz APIs only come in async flavor.
targz().compress(yeomanOutputRoot, path.join(artifactsDir, 'generator-aspnetcore-spa.tar.gz'), err => {
if (err) { throw err; }
});

View File

@@ -3,7 +3,6 @@ Generates ASP.NET Core projects for single-page applications.
Can generate projects with:
* Angular 2
* Aurelia
* Knockout
* React
* React with Redux

View File

@@ -4,13 +4,11 @@ import * as yeoman from 'yeoman-generator';
import * as uuid from 'node-uuid';
import * as glob from 'glob';
import * as semver from 'semver';
import * as chalk from 'chalk';
import { execSync } from 'child_process';
import npmWhich = require('npm-which');
const yosay = require('yosay');
const toPascalCase = require('to-pascal-case');
const isWindows = /^win/.test(process.platform);
const generatorPackageJson = require(path.resolve(__dirname, '../package.json'));
// Paths matching these regexes will only be included if the user wants tests
const testSpecificPaths = [
@@ -43,20 +41,6 @@ const templates = [
{ value: 'react-redux', name: 'React with Redux', tests: false }
];
// Once everyone is on .csproj-compatible tooling, we might be able to remove the global.json files and eliminate
// this SDK choice altogether. That would be good because then it would work with whatever SDK version you have
// installed. For now, we need to specify an SDK version explicitly, because there's no support for wildcards, and
// preview3+ tooling doesn't support project.json at all.
const sdkChoices = [{
value: '1.0.0-preview2-1-003177', // Current released version
name: 'project.json' + chalk.gray(' (compatible with .NET Core tooling preview 2 and Visual Studio 2015)'),
includeFiles: [/^project.json$/, /\.xproj$/, /_placeholder.txt$/, /\.deployment$/]
}, {
value: '1.0.0-preview3-004056', // Version that ships with VS2017RC
name: '.csproj' + chalk.gray(' (compatible with .NET Core tooling preview 3 and Visual Studio 2017)'),
includeFiles: [/\.csproj$/]
}];
class MyGenerator extends yeoman.Base {
private _answers: any;
private _optionOrPrompt: YeomanPrompt;
@@ -64,7 +48,7 @@ class MyGenerator extends yeoman.Base {
constructor(args: string | string[], options: any) {
super(args, options);
this._optionOrPrompt = optionOrPrompt;
this.log(yosay('Welcome to the ASP.NET Core Single-Page App generator!\n\nVersion: ' + generatorPackageJson.version));
this.log(yosay('Welcome to the ASP.NET Core Single-Page App generator!'));
if (isWindows) {
assertNpmVersionIsAtLeast('3.0.0');
@@ -80,13 +64,8 @@ class MyGenerator extends yeoman.Base {
name: 'framework',
message: 'Framework',
choices: templates
}, {
type: 'list',
name: 'sdkVersion',
message: 'What type of project do you want to create?',
choices: sdkChoices
}], firstAnswers => {
const frameworkChoice = templates.filter(t => t.value === firstAnswers.framework)[0];
}], frameworkAnswer => {
const frameworkChoice = templates.filter(t => t.value === frameworkAnswer.framework)[0];
const furtherQuestions = [{
type: 'input',
name: 'name',
@@ -104,10 +83,9 @@ class MyGenerator extends yeoman.Base {
}
this._optionOrPrompt(furtherQuestions, answers => {
answers.framework = firstAnswers.framework;
answers.framework = frameworkAnswer.framework;
this._answers = answers;
this._answers.framework = firstAnswers.framework;
this._answers.sdkVersion = firstAnswers.sdkVersion;
this._answers.framework = frameworkAnswer.framework;
this._answers.namePascalCase = toPascalCase(answers.name);
this._answers.projectGuid = this.options['projectguid'] || uuid.v4();
done();
@@ -116,8 +94,7 @@ class MyGenerator extends yeoman.Base {
}
writing() {
const templateRoot = this.templatePath(this._answers.framework);
const chosenSdk = sdkChoices.filter(sdk => sdk.value === this._answers.sdkVersion)[0];
var templateRoot = this.templatePath(this._answers.framework);
glob.sync('**/*', { cwd: templateRoot, dot: true, nodir: true }).forEach(fn => {
// Token replacement in filenames
let outputFn = fn.replace(/tokenreplace\-([^\.\/]*)/g, (substr, token) => this._answers[token]);
@@ -127,17 +104,23 @@ class MyGenerator extends yeoman.Base {
outputFn = path.join(path.dirname(fn), '.gitignore');
}
// Decide whether to emit this file
const isTestSpecificFile = testSpecificPaths.some(regex => regex.test(outputFn));
const isSdkSpecificFile = sdkChoices.some(sdk => sdk.includeFiles.some(regex => regex.test(outputFn)));
const matchesChosenSdk = chosenSdk.includeFiles.some(regex => regex.test(outputFn));
const emitFile = (matchesChosenSdk || !isSdkSpecificFile)
&& (this._answers.tests || !isTestSpecificFile);
// Likewise, output template_nodemodules_placeholder.txt as node_modules/_placeholder.txt
// This is a workaround for https://github.com/aspnet/JavaScriptServices/issues/235. We need the new project
// to have a nonempty node_modules dir as far as *source control* is concerned. So, there's a gitignore
// rule that explicitly causes node_modules/_placeholder.txt to be tracked in source control. But how
// does that file get there in the first place? It's not enough for such a file to exist when the
// generator-aspnetcore-spa NPM package is published, because NPM doesn't allow any directories called
// node_modules to exist in the package. So we have a file with at a different location, and move it
// to node_modules as part of executing the template.
if (path.basename(fn) === 'template_nodemodules_placeholder.txt') {
outputFn = path.join(path.dirname(fn), 'node_modules', '_placeholder.txt');
}
if (emitFile) {
// Exclude test-specific files (unless the user has said they want tests)
const isTestSpecificFile = testSpecificPaths.some(regex => regex.test(outputFn));
if (this._answers.tests || !isTestSpecificFile) {
let inputFullPath = path.join(templateRoot, fn);
let destinationFullPath = this.destinationPath(outputFn);
let deleteInputFileAfter = false;
if (path.basename(fn) === 'package.json') {
// Special handling for package.json, because we rewrite it dynamically
const tempPath = destinationFullPath + '.tmp';
@@ -148,7 +131,6 @@ class MyGenerator extends yeoman.Base {
/* space */ 2
);
inputFullPath = tempPath;
deleteInputFileAfter = true;
}
const outputDirBasename = path.basename(path.dirname(destinationFullPath));
@@ -166,10 +148,6 @@ class MyGenerator extends yeoman.Base {
this._answers
);
}
if (deleteInputFileAfter) {
this.fs.delete(inputFullPath);
}
}
});
}
@@ -208,7 +186,7 @@ function assertNpmVersionIsAtLeast(minVersion: string) {
const runningVersion = execSync('npm -v').toString();
if (!semver.gte(runningVersion, minVersion, /* loose */ true)) {
console.error(`This generator requires NPM version ${minVersion} or later. You are running NPM version ${runningVersion}`);
process.exit(1);
process.exit(0);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "generator-aspnetcore-spa",
"version": "0.7.3",
"version": "0.7.1",
"description": "Single-Page App templates for ASP.NET Core",
"author": "Microsoft",
"license": "Apache-2.0",
@@ -18,7 +18,6 @@
"url": "https://github.com/aspnet/JavaScriptServices.git"
},
"dependencies": {
"chalk": "^1.1.3",
"glob": "^7.0.3",
"node-uuid": "^1.4.7",
"npm-which": "^3.0.1",

View File

@@ -7,7 +7,6 @@
"lib": ["es6"]
},
"exclude": [
"node_modules",
"**/node_modules",
"dist"
]

3
test/.gitignore vendored
View File

@@ -1,3 +0,0 @@
/node_modules/
/tmp/
/yarn.lock

View File

@@ -1,34 +0,0 @@
{
"name": "test",
"version": "1.0.0",
"description": "Integration tests for the templates in JavaScriptServices. This is not really an NPM package and will not be published.",
"main": "index.js",
"scripts": {
"test": "tsc && wdio"
},
"author": "Microsoft",
"license": "Apache-2.0",
"dependencies": {
"@types/chai": "^3.4.34",
"@types/mkdirp": "^0.3.29",
"@types/mocha": "^2.2.33",
"@types/node": "^6.0.52",
"@types/rimraf": "^0.0.28",
"@types/webdriverio": "^4.0.32",
"chai": "^3.5.0",
"cross-spawn": "^5.0.1",
"mkdirp": "^0.5.1",
"portastic": "^1.0.1",
"rimraf": "^2.5.4",
"selenium-standalone": "^5.9.0",
"tree-kill": "^1.1.0",
"typescript": "^2.1.4",
"webdriverio": "^4.5.0",
"yo": "^1.8.5"
},
"devDependencies": {
"wdio-junit-reporter": "^0.2.0",
"wdio-mocha-framework": "^0.5.7",
"wdio-selenium-standalone-service": "0.0.7"
}
}

View File

@@ -1,106 +0,0 @@
import * as fs from 'fs';
import * as path from 'path';
import { expect } from 'chai';
import { generateProjectSync } from './util/yeoman';
import { AspNetProcess, AspNetCoreEnviroment, defaultUrl, publishProjectSync } from './util/aspnet';
import { getValue, getCssPropertyValue } from './util/webdriverio';
// Currently we test both 'csproj' and 'project.json' project types. Eventually we'll only need csproj.
['csproj', 'projectjson'].forEach(toolingType => {
// First, generate a new project using the locally-built generator-aspnetcore-spa
// Do this outside the Mocha fixture, otherwise Mocha will time out
const appDir = path.resolve(__dirname, '../generated/angular', toolingType);
const publishedAppDir = path.resolve(appDir, './bin/Release/published');
if (!process.env.SKIP_PROJECT_GENERATION) {
generateProjectSync(appDir, {
framework: 'angular-2',
name: 'Test App',
sdkVersion: toolingType === 'projectjson' ? '1.0.0-preview2-1-003177' : '1.0.0-preview3-004056',
tests: false
});
publishProjectSync(appDir, publishedAppDir);
}
function testBasicNavigation() {
describe('Basic navigation', () => {
beforeEach(() => browser.url(defaultUrl));
it('should initially display the home page', () => {
expect(browser.getText('h1')).to.eq('Hello, world!');
expect(browser.getText('li a[href="https://angular.io/"]')).to.eq('Angular 2');
});
it('should be able to show the counter page', () => {
browser.click('a[href="/counter"]');
expect(browser.getText('h1')).to.eq('Counter');
// Test clicking the 'increment' button
expect(browser.getText('counter strong')).to.eq('0');
browser.click('counter button');
expect(browser.getText('counter strong')).to.eq('1');
});
it('should be able to show the fetchdata page', () => {
browser.click('a[href="/fetch-data"]');
expect(browser.getText('h1')).to.eq('Weather forecast');
browser.waitForExist('fetchdata table');
expect(getValue(browser.elements('fetchdata table tbody tr')).length).to.eq(5);
});
});
}
function testHotModuleReplacement() {
describe('Hot module replacement', () => {
beforeEach(() => browser.url(defaultUrl));
it('should update when HTML is changed', () => {
expect(browser.getText('h1')).to.eq('Hello, world!');
const filePath = path.resolve(appDir, './ClientApp/app/components/home/home.component.html');
const origFileContents = fs.readFileSync(filePath, 'utf8');
try {
const newFileContents = origFileContents.replace('<h1>Hello, world!</h1>', '<h1>HMR is working</h1>');
fs.writeFileSync(filePath, newFileContents, { encoding: 'utf8' });
browser.waitUntil(() => browser.getText('h1').toString() === 'HMR is working');
} finally {
// Restore old contents so that other tests don't have to account for this
fs.writeFileSync(filePath, origFileContents, { encoding: 'utf8' });
}
});
it('should update when CSS is changed', () => {
expect(getCssPropertyValue(browser, 'li.link-active a', 'color')).to.eq('rgba(255,255,255,1)');
const filePath = path.resolve(appDir, './ClientApp/app/components/navmenu/navmenu.component.css');
const origFileContents = fs.readFileSync(filePath, 'utf8');
try {
const newFileContents = origFileContents.replace('color: white;', 'color: purple;');
fs.writeFileSync(filePath, newFileContents, { encoding: 'utf8' });
browser.waitUntil(() => getCssPropertyValue(browser, 'li.link-active a', 'color') === 'rgba(128,0,128,1)');
} finally {
// Restore old contents so that other tests don't have to account for this
fs.writeFileSync(filePath, origFileContents, { encoding: 'utf8' });
}
});
});
}
// Now launch dotnet and use selenium to perform tests
describe('Angular template: dev mode', () => {
AspNetProcess.RunInMochaContext(appDir, AspNetCoreEnviroment.development);
testBasicNavigation();
testHotModuleReplacement();
});
describe('Angular template: production mode', () => {
// csproj tooling takes the assembly name from <name>.csproj, whereas project.json takes it from the directory name
const assemblyName = toolingType === 'csproj' ? 'TestApp.dll' : 'projectjson.dll';
AspNetProcess.RunInMochaContext(publishedAppDir, AspNetCoreEnviroment.production, assemblyName);
testBasicNavigation();
});
});

View File

@@ -1,123 +0,0 @@
import * as childProcess from 'child_process';
import * as path from 'path';
import * as readline from 'readline';
import { waitUntilPortState } from './ports';
const treeKill = require('tree-kill');
const crossSpawn: typeof childProcess.spawn = require('cross-spawn');
const defaultPort = 5000;
const defaultInterface = 'localhost';
export const defaultUrl = `http://localhost:${ defaultPort }`;
export enum AspNetCoreEnviroment {
development,
production
}
export class AspNetProcess {
public static RunInMochaContext(cwd: string, mode: AspNetCoreEnviroment, dllToRun?: string) {
// Set up mocha before/after callbacks so that a 'dotnet run' process exists
// for the same duration as the context this is called inside
let aspNetProcess: AspNetProcess;
before(() => {
aspNetProcess = new AspNetProcess(cwd, mode, dllToRun);
return aspNetProcess.waitUntilListening();
});
after(() => aspNetProcess.dispose());
}
private _process: childProcess.ChildProcess;
private _processHasExited: boolean;
private _stdoutReader: readline.ReadLine;
constructor(cwd: string, mode: AspNetCoreEnviroment, dllToRun?: string) {
try {
// Prepare env for child process. Note that it doesn't inherit parent's env vars automatically,
// hence cloning process.env.
const childProcessEnv = Object.assign({}, process.env);
childProcessEnv.ASPNETCORE_ENVIRONMENT = mode === AspNetCoreEnviroment.development ? 'Development' : 'Production';
const verbOrAssembly = dllToRun || 'run';
console.log(`Running 'dotnet ${ verbOrAssembly }' in ${ cwd }`);
this._process = crossSpawn('dotnet', [verbOrAssembly], { cwd: cwd, stdio: 'pipe', env: childProcessEnv });
this._stdoutReader = readline.createInterface(this._process.stdout, null);
// Echo stdout to the test process's own stdout
this._stdoutReader.on('line', line => {
console.log(`[dotnet] ${ line.toString() }`);
});
// Also echo stderr
this._process.stderr.on('data', chunk => {
console.log(`[dotnet ERROR] ${ chunk.toString() }`);
});
// Ensure the process isn't orphaned even if Node crashes before we're disposed
process.on('exit', () => this._killAspNetProcess());
// Also track whether it exited on its own already
this._process.on('exit', () => {
this._processHasExited = true;
});
} catch(ex) {
console.log('ERROR: ' + ex.toString());
throw ex;
}
}
public waitUntilListening(): Promise<any> {
return new Promise((resolve, reject) => {
this._stdoutReader.on('line', (line: string) => {
if (line.startsWith('Now listening on:')) {
resolve();
}
});
});
}
public dispose(): Promise<any> {
return new Promise((resolve, reject) => {
this._killAspNetProcess(err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
private _killAspNetProcess(callback?: (err: any) => void) {
callback = callback || (() => {});
if (!this._processHasExited) {
// It's important to kill the whole tree, because 'dotnet run' launches a separate 'dotnet exec'
// child process that would otherwise be left running
treeKill(this._process.pid, 'SIGINT', err => {
if (err) {
callback(err);
} else {
// It's not enough just to send a SIGINT to ASP.NET. It will stay open for a moment, completing
// any outstanding requests. We have to wait for it really to be gone before continuing, otherwise
// the next test might be unable to start because of the port still being in use.
console.log(`Waiting until port ${ defaultPort } is closed...`);
waitUntilPortState(defaultPort, defaultInterface, /* isListening */ false, /* timeoutMs */ 15000, err => {
if (err) {
callback(err);
} else {
console.log(`Port ${ defaultPort } is now closed`);
callback(null);
}
});
}
});
}
}
}
export function publishProjectSync(sourceDir: string, outputDir: string) {
childProcess.execSync(`dotnet publish -c Release -o ${ outputDir }`, {
cwd: sourceDir,
stdio: 'inherit',
encoding: 'utf8'
});
}

View File

@@ -1,24 +0,0 @@
import * as portastic from 'portastic';
const pollInterval = 500;
export function waitUntilPortState(port: number, iface: string, isListening: boolean, timeoutMs: number, callback: (err: any) => void) {
if (!(timeoutMs > 0)) {
throw new Error(`Timed out waiting for port ${ port } to become ${ isListening ? 'in use' : 'free' }`);
}
portastic.test(port, iface).then(
actuallyIsAvailable => {
const actuallyIsListening = !actuallyIsAvailable;
if (actuallyIsListening === isListening) {
// Desired state is reached
callback(null);
} else {
// Wait longer
setTimeout(() => {
waitUntilPortState(port, iface, isListening, timeoutMs - pollInterval, callback);
}, pollInterval);
}
},
callback
)
}

View File

@@ -1,12 +0,0 @@
// Workaround for missing '.value' property on WebdriverIO.Client<RawResult<T>> that should be of type T
// Can't notify TypeScript that the property exists directly, because the interface merging feature doesn't
// appear to support pattern matching in such a way that WebdriverIO.Client<T> is extended only when T
// itself extends RawResult<U> for some U.
export function getValue<T>(client: WebdriverIO.Client<WebdriverIO.RawResult<T>>): T {
return (client as any).value;
}
// The official type declarations for getCssProperty are completely wrong. This function matches runtime behaviour.
export function getCssPropertyValue<T>(client: WebdriverIO.Client<T>, selector: string, cssProperty: string): string {
return (client.getCssProperty(selector, cssProperty) as any).value;
}

View File

@@ -1,53 +0,0 @@
import * as childProcess from 'child_process';
import * as path from 'path';
import * as rimraf from 'rimraf';
import * as mkdirp from 'mkdirp';
const generatorDirRelative = '../templates/package-builder/dist/generator-aspnetcore-spa';
const yoPackageDirAbsolute = path.resolve('./node_modules/yo');
export interface GeneratorOptions {
framework: string;
name: string;
sdkVersion?: string;
tests?: boolean;
}
export function generateProjectSync(targetDir: string, generatorOptions: GeneratorOptions) {
const generatorDirAbsolute = path.resolve(generatorDirRelative);
console.log(`Running NPM install to prepare Yeoman generator at ${ generatorDirAbsolute }`);
childProcess.execSync(`npm install`, { stdio: 'inherit', cwd: generatorDirAbsolute });
console.log(`Ensuring empty output directory at ${ targetDir }`);
rimraf.sync(targetDir);
mkdirp.sync(targetDir);
const yoExecutableAbsolute = findYeomanCliScript();
console.log(`Will invoke Yeoman at ${ yoExecutableAbsolute } to generate application in ${ targetDir } with options:`);
console.log(JSON.stringify(generatorOptions, null, 2));
const command = `node "${ yoExecutableAbsolute }" "${ path.resolve(generatorDirAbsolute, './app/index.js') }"`;
const args = makeYeomanCommandLineArgs(generatorOptions);
childProcess.execSync(`${ command } ${ args }`, {
stdio: 'inherit',
cwd: targetDir
});
}
function findYeomanCliScript() {
// On Windows, you can't invoke ./node_modules/.bin/yo from the shell for some reason.
// So instead, we'll locate the CLI entrypoint that yeoman would expose if it was installed globally.
const yeomanPackageJsonPath = path.join(yoPackageDirAbsolute, './package.json');
const yeomanPackageJson = require(yeomanPackageJsonPath);
const yeomanCliScriptRelative = yeomanPackageJson.bin.yo;
if (!yeomanCliScriptRelative) {
throw new Error(`Could not find Yeoman CLI script. Looked for a bin/yo entry in ${ yeomanPackageJsonPath }`);
}
return path.join(yoPackageDirAbsolute, yeomanCliScriptRelative);
}
function makeYeomanCommandLineArgs(generatorOptions: GeneratorOptions) {
return Object.getOwnPropertyNames(generatorOptions)
.map(key => `--${ key }="${ generatorOptions[key] }"`)
.join(' ');
}

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"rootDir": ".",
"outDir": "tmp",
"sourceMap": false,
"lib": ["es6", "dom"]
},
"exclude": [
"node_modules",
"**/node_modules",
"tmp"
]
}

View File

@@ -1,204 +0,0 @@
exports.config = {
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
specs: [
'./tmp/templates/**/*.spec.js'
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
// maxInstances can get overwritten per capability. So if you have an in-house Selenium
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 5,
//
browserName: 'chrome'
}],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// By default WebdriverIO commands are executed in a synchronous way using
// the wdio-sync package. If you still want to run your tests in an async way
// e.g. using promises you can set the sync option to false.
sync: true,
//
// Level of logging verbosity: silent | verbose | command | data | result | error
logLevel: 'silent',
//
// Enables colors for log output.
coloredLogs: true,
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Saves a screenshot to a given path if a command fails.
screenshotPath: './tmp/errorShots/',
//
// Set a base URL in order to shorten url command calls. If your url parameter starts
// with "/", then the base url gets prepended.
baseUrl: 'http://localhost:5000',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if Selenium Grid doesn't send response
connectionRetryTimeout: 90000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Initialize the browser instance with a WebdriverIO plugin. The object should have the
// plugin name as key and the desired plugin options as properties. Make sure you have
// the plugin installed before running any tests. The following plugins are currently
// available:
// WebdriverCSS: https://github.com/webdriverio/webdrivercss
// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
// Browserevent: https://github.com/webdriverio/browserevent
// plugins: {
// webdrivercss: {
// screenshotRoot: 'my-shots',
// failedComparisonsRoot: 'diffs',
// misMatchTolerance: 0.05,
// screenWidth: [320,480,640,1024]
// },
// webdriverrtc: {},
// browserevent: {}
// },
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
// services: ['selenium-standalone'],
//
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: http://webdriver.io/guide/testrunner/frameworks.html
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: 'mocha',
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: http://webdriver.io/guide/testrunner/reporters.html
reporters: ['junit'],
reporterOptions: {
outputDir: './tmp/junit'
},
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
ui: 'bdd',
timeout: 60000
},
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
//
// Gets executed once before all workers get launched.
// onPrepare: function (config, capabilities) {
// },
//
// Gets executed just before initialising the webdriver session and test framework. It allows you
// to manipulate configurations depending on the capability or spec.
// beforeSession: function (config, capabilities, specs) {
// },
//
// Gets executed before test execution begins. At this point you can access all global
// variables, such as `browser`. It is the perfect place to define custom commands.
// before: function (capabilities, specs) {
// },
//
// Hook that gets executed before the suite starts
// beforeSuite: function (suite) {
// },
//
// Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
// beforeEach in Mocha)
// beforeHook: function () {
// },
//
// Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
// afterEach in Mocha)
// afterHook: function () {
// },
//
// Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
// beforeTest: function (test) {
// },
//
// Runs before a WebdriverIO command gets executed.
// beforeCommand: function (commandName, args) {
// },
//
// Runs after a WebdriverIO command gets executed
// afterCommand: function (commandName, args, result, error) {
// },
//
// Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
// afterTest: function (test) {
// },
//
// Hook that gets executed after the suite has ended
// afterSuite: function (suite) {
// },
//
// Gets executed after all tests are done. You still have access to all global variables from
// the test.
// after: function (result, capabilities, specs) {
// },
//
// Gets executed right after terminating the webdriver session.
// afterSession: function (config, capabilities, specs) {
// },
//
// Gets executed after all workers got shut down and the process is about to exit. It is not
// possible to defer the end of the process using a promise.
// onComplete: function(exitCode) {
// }
}