From de61ab8744a4c3262326aa948b4f509e3e61eee3 Mon Sep 17 00:00:00 2001 From: Steve Ognibene Date: Sun, 15 May 2016 15:53:09 -0400 Subject: [PATCH] Prep for v5.5.1 --- CHANGELOG.md | 7 + Gruntfile.js | 6 +- README.md | 206 +++++++++++++++++- package.json | 2 +- tasks-internal/modules/amdLoader.js | 32 +-- tasks-internal/modules/amdLoader.ts | 41 ++-- tasks-internal/modules/cacheUtils.js | 1 + tasks-internal/modules/compile.js | 112 ++++++++-- tasks-internal/modules/compile.ts | 125 ++++++++--- tasks-internal/modules/defaults.js | 27 ++- tasks-internal/modules/defaults.ts | 27 ++- tasks-internal/modules/html2ts.js | 11 +- tasks-internal/modules/html2ts.ts | 11 +- tasks-internal/modules/interfaces.d.ts | 37 +++- tasks-internal/modules/optionsResolver.js | 126 +++++++---- tasks-internal/modules/optionsResolver.ts | 132 ++++++++--- tasks-internal/modules/reference.js | 1 + tasks-internal/modules/templateCache.js | 1 + tasks-internal/modules/transformers.js | 13 +- tasks-internal/modules/tsconfig.js | 134 +++++++----- tasks-internal/modules/tsconfig.ts | 151 ++++++++----- tasks-internal/modules/utils.js | 25 ++- tasks-internal/modules/utils.ts | 19 +- .../modules/visualStudioOptionsResolver.js | 30 ++- .../modules/visualStudioOptionsResolver.ts | 15 +- tasks-internal/ts-internal.js | 48 ++-- tasks-internal/ts.ts | 43 ++-- tasks/modules/utils.js | 1 + tasks/modules/utils.ts | 1 + 29 files changed, 1034 insertions(+), 351 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6e30e0..5ae2fc2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Releases +## vNext + +## v5.5.1 +* CHORE: Internal grunt-ts compiler now upgraded to v5.5.0 / TypeScript 1.8.9. +* CHORE: Grunt-ts itself now compiles cleanly with `--forceConsistentCasingInFileNames`, `--noFallthroughCasesInSwitch`, and `--noImplicitReturns` enabled. +* DOCS: Completed documentation for new v5.5 (TypeScript 1.8) features. + ## v5.5.0 * FEAT: Support TypeScript 1.8+ * FIX: "Visual Studio config issue: {} when src contains nested arrays". Thanks very much to first-time contributor @davidparsson for the PR! (#353) diff --git a/Gruntfile.js b/Gruntfile.js index 0dbcc087..883c8a3e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -59,7 +59,11 @@ module.exports = function (grunt) { comments: true, sourceMap: true, verbose: true, - fast: 'always' + fast: 'always', + forceConsistentCasingInFileNames: true, + noFallthroughCasesInSwitch: true, + noImplicitReturns: true, + pretty: true }, build: { src: ['tasks/**/*.ts'] diff --git a/README.md b/README.md index 09cc36c2..03b2fffa 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Grunt-ts is an npm package that handles TypeScript compilation work in GruntJS build scripts. It provides a [Grunt-compatible wrapper](#support-for-tsc-switches) for the `tsc` command-line compiler, and provides some [additional functionality](#grunt-ts-gruntfilejs-options) that improves the TypeScript development workflow. Grunt-ts supports compiling against [tsconfig.json](#tsconfig) or even a [Visual Studio project](#vs) directly. Grunt-ts is itself written in [TypeScript](./tasks/ts.ts). ### Latest Changes -Latest release is `5.5.0` with built-in support for features added in TypeScript 1.8. [Full changelog is here](CHANGELOG.md). +Latest release is `5.5.1` with built-in support for features added in TypeScript 1.8. [Full changelog is here](CHANGELOG.md). ### How To Contribute Thank you for your interest in contributing! Please see the [contributing](CONTRIBUTING.md) guide for details. @@ -27,7 +27,7 @@ module.exports = function(grunt) { grunt.initConfig({ ts: { default : { - src: ["**/*.ts", "!node_modules/**/*.ts"] + src: ["**/*.ts", "!node_modules/**"] } } }); @@ -60,11 +60,15 @@ Grunt-ts provides explicit support for most `tsc` switches. Any arbitrary switc |`tsc` switch|name in grunt-ts|description| |:----:|:----:|:-----| +|--allowJs|[allowJs](#allowjs)|Allow JavaScript files (*.js) to be compiled.| |--allowSyntheticDefaultImports|[allowSyntheticDefaultImports](#allowsyntheticdefaultimports)|Allows use "default" ES6 module import syntax with pre-ES6 libraries that don't have a default (on by default with SystemJS)| +|--allowUnreachableCode|[allowUnreachableCode](#allowunreachablecode)|Do not report errors on unreachable code.| +|--allowUnusedLabels|[allowUnusedLabels](#allowunusedlabels)|Do not report errors on unused labels.| |--declaration|[declaration](#declaration)|Generates a `.d.ts` definitions file for compiled TypeScript files| |--emitDecoratorMetadata|[emitDecoratorMetadata](#emitdecoratormetadata)|Emit metadata for type/parameter decorators.| -|--experimentalAsyncFunctions|[experimentalAsyncFunctions](#experimentalasyncfunctions)|Enables experimental support for ES7 async functions| -|--experimentalDecorators|[experimentalDecorators](#experimentaldecorators)|Enables experimental support for ES7 decorators| +|--experimentalAsyncFunctions|[experimentalAsyncFunctions](#experimentalasyncfunctions)|Enables experimental support for proposed ECMAScript async functions| +|--experimentalDecorators|[experimentalDecorators](#experimentaldecorators)|Enables experimental support for proposed ECMAScript decorators| +|--forceConsistentCasingInFileNames|[forceConsistentCasingInFileNames](#forceConsistentCasingInFileNames)|Disallow inconsistently-cased references to the same file.| |--inlineSourceMap|[inlineSourceMap](#inlinesourcemap)|Emit a single file that includes source maps instead of emitting a separate `.js.map` file.| |--inlineSources|[inlineSources](#inlinesources)|Emit the TypeScript source alongside the sourcemaps within a single file; requires `--inlineSourceMap` to be set.| |--isolatedModules|[isolatedModules](#isolatedmodules)|Ensures that the output is safe to only emit single files by making cases that break single-file transpilation an error| @@ -77,14 +81,20 @@ Grunt-ts provides explicit support for most `tsc` switches. Any arbitrary switc |--noEmit|[noEmit](#noemit)|Check, but do not emit JS, even in the absence of errors.| |--noEmitHelpers|[noEmitHelpers](#noemithelpers)|Do not generate custom helper functions like `__extends` in compiled output.| |--noEmitOnError|[noEmitOnError](#noemitonerror)|Do not emit JavaScript if there is a compilation error| +|--noFallthroughCasesInSwitch|[noFallthroughCasesInSwitch](#nofallthroughcasesinswitch)|Report errors for fallthrough cases in switch statement.| |--noImplicitAny|[noImplicitAny](#noimplicitany)|Warn on expressions and declarations with an implied `any` type.| +|--noImplicitUseStrict|[noImplicitUseStrict](#noimplicitusestrict)|Warn on expressions and declarations with an implied `any` type.| +|--noImplicitReturns|[noImplicitReturns](#noimplicitreturns)|Report error when not all code paths in function return a value.| |--noLib|[noLib](#nolib)|Do not automatically include lib.d.ts is compilation context.| |--noResolve|[noResolve](#noresolve)|Do not add triple-slash references or module import targets to the compilation context.| |--out FILE|[out](#out)|Concatenate and emit output to a single file.| |--outDir DIRECTORY|[outDir](#outdir)|Redirect output structure to the directory.| |--preserveConstEnums|[preserveConstEnums](#preserveconstenums)|Const enums will be kept as enums in the emitted JS.| +|--pretty|[pretty](#pretty)|Stylize errors and messages using color and context.| +|--reactNamespace|[reactNamespace](#reactnamespace)|Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit.| |--removeComments|[removeComments](#removecomments)|Configures if comments should be included in the output| |--rootDir|[rootDir](#rootdir)|Allows override of common root folder calculated by `--outDir`.| +|--skipDefaultLibCheck|[skipDefaultLibCheck](#skipdefaultlibcheck)|Don't check a user-defined default lib file's validity.| |--sourceMap|[sourceMap](#sourcemap)|Generates corresponding `.map` file| |--sourceRoot LOCATION|[sourceRoot](#sourceroot)|Specifies the location where debugger should locate TypeScript files instead of source locations.| |--stripInternal|[stripInternal](#stripinternal)|does not emit members marked as @internal.| @@ -100,19 +110,23 @@ For file ordering, look at [JavaScript Generation](#javascript-generation). |grunt-ts property|where to define|description| |:----|:----|:-----| |[additionalFlags](#additionalflags)|option|`string` - allows passing arbitrary strings to the compiler. This is intended to enable compatibility with features not supported directly by grunt-ts.| +|[allowJs](#allowjs)|option|`true`, `false` (default) - Allow JavaScript files (*.js) to be compiled.| +|[allowUnreachableCode](#allowunreachablecode)|option|`true`, `false` (default) - Do not report errors on unreachable code.| +|[allowUnusedLabels](#allowunusedlabels)|option|`true`, `false` (default) - Do not report errors on unused labels.| |[allowSyntheticDefaultImports](#allowsyntheticdefaultimports)|option|`true`, `false` (default) - Allows use "default" ES6 module import syntax with pre-ES6 libraries that don't have a default (on by default with SystemJS and not required to specify).| |[baseDir](#basedir)|option|`string` - Sets root directory for maintaining source structure when using outDir and fast together. Use `rootDir` for newer versions of TypeScript.| |[comments](#comments)|option|`true`, `false` (default) - include comments in emitted JS.| |[compile](#compile)|option|`true` (default), `false` - compile TypeScript code.| |[compiler](#compiler)|option|`string` - path to custom compiler| |[declaration](#declaration)|option|`true`, `false` (default) - indicates that definition files should be emitted.| -|[emitDecoratorMetadata](#emitdecoratormetadata)|option|`true`, `false` (default) - set to true to emit metadata for ES7 decorators (will enable experimentalDecorators)| +|[emitDecoratorMetadata](#emitdecoratormetadata)|option|`true`, `false` (default) - set to true to emit metadata for proposed ECMAScript decorators (will enable experimentalDecorators)| |[emitGruntEvents](#emitgruntevents)|option|`true`, `false` (default) - set to true to raise an event in Grunt upon failed builds.| -|[experimentalAsyncFunctions](#experimentalasyncfunctions)|option|`true`, `false` (default) - set to true to enable support for ES7 async functions (in ES6 mode only)| -|[experimentalDecorators](#experimentaldecorators)|option|`true`, `false` (default) - set to true to enable support for ES7 decorators| +|[experimentalAsyncFunctions](#experimentalasyncfunctions)|option|`true`, `false` (default) - set to true to enable support for proposed ECMAScript async functions (in ES6 mode only)| +|[experimentalDecorators](#experimentaldecorators)|option|`true`, `false` (default) - set to true to enable support for proposed ECMAScript decorators| |[failOnTypeErrors](#failontypeerrors)|option|`true` (default), `false` - fail Grunt pipeline if there is a type error. (See also [noEmitOnError](#noemithelpers))| |[fast](#fast)|option|`'watch'` (default), `'always'`, `'never'` - how to decide on a "fast" grunt-ts compile.| |[files](#files)|target|Sets of files to compile and optional output destination| +|[forceConsistentCasingInFileNames](#forceconsistentcasinginfilenames)|option|`true`, `false` (default) - Disallow inconsistently-cased references to the same file.| |[html](#html)|target|`string` or `string[]` - glob to HTML templates| |[htmlModuleTemplate](#htmlmoduletemplate)|option|`string` - HTML template namespace| |[htmlOutDir](#htmloutdir)|option|`string` - Sets a root for output of transformed-to-TypeScript HTML files| @@ -130,16 +144,21 @@ For file ordering, look at [JavaScript Generation](#javascript-generation). |[noEmit](#noemit)|option|`true`, `false` (default) - If passed as `true`, TypeScript will not emit even if it compiles cleanly| |[noEmitHelpers](#noemithelpers)|option|`true`, `false` (default) - If passed as `true`, TypeScript will not generate custom helper functions like `__extends` in compiled output| |[noEmitOnError](#noemithelpers)|option|`true`, `false` (default) - If passed as `true`, TypeScript will not emit JS if there is an error (see also [failOnTypeErrors](#failontypeerrors))| +|[noFallthroughCasesInSwitch](#nofallthroughcasesinswitch)|option|`true`, `false` (default) - Report errors for fallthrough cases in switch statement.| |[noImplicitAny](#noimplicitany)|option|`true`, `false` (default) - enable for stricter type checking| +|[noImplicitReturns](#noimplicitreturns)|option|`true`, `false` (default) - Report error when not all code paths in function return a value.| |[noLib](#nolib)|option|`true`, `false` (default) - do not automatically include lib.d.ts in compilation context| |[noResolve](#noresolve)|option|`true`, `false` (default) - for deprecated version of TypeScript| |[options](#grunt-ts-target-options)|target|| |[out](#out)|target|`string` - instruct `tsc` to concatenate output to this file.| |[outDir](#outdir)|target|`string` - instruct `tsc` to emit JS to this directory.| |[preserveConstEnums](#preserveconstenums)|option|`true`, `false` (default) - If true, const enums will be kept as enums in the emitted JS.| +|[pretty](#pretty)|option|`true`, `false` (default) - Stylize errors and messages using color and context.| +|[reactNamespace](#reactnamespace)|option|`string` - Specifies the object invoked for `createElement` and `__spread` when targeting 'react' JSX emit.| |[reference](#reference)|target|`string` - tells grunt-ts which file to use for maintaining references| |[removeComments](#removecomments)|option|`true` (default), `false` - removes comments in emitted JS| |[rootDir](#rootdir)|option|`string` - Allows override of common root folder calculated by `--outDir`.| +|[skipDefaultLibCheck](#skipdefaultlibcheck)|option|`true`, `false` (default) - Don't check a user-defined default lib file's validity.| |[sourceRoot](#sourceroot)|option|`string` - root for referencing TS files in `.js.map`| |[sourceMap](#sourcemap)|option|`true` (default), `false` - indicates if source maps should be generated (`.js.map`)| |[stripInternal](#stripinternal)|option|`true`, `false` (default) - does not emit members marked as @internal.| @@ -413,6 +432,24 @@ grunt.initConfig({ }); ```` +#### allowJs + +Allows JavaScript files to be compiled. This setting works well with `outDir`. This feature requires grunt-ts 5.5 or higher and TypeScript 1.8 or higher. + +````javascript +grunt.initConfig({ + ts: { + default: { + src: ["**/*.ts", "**/*.js", "!emit/**", "!node_modules/**"], + outDir: 'emit/', + options: { + allowJs: true + } + } + } +}); +```` + #### allowSyntheticDefaultImports Allows use of ES6 "default" import syntax with pre-ES6 modules when not using SystemJS. If using module format "amd", "commonjs" or "umd", the following import syntax for jQuery will give the error "Module 'jquery' has no default export" when exporting to "amd", "commonjs", or "umd" format: `import * as $ from 'jquery';`. In that case, passing allowSyntheticDefaultImports will eliminate this error. NOTE: This is the default behavior when SystemJS module format is used (`module: "system"`). This switch (and behavior) requires TypeScript 1.8 or higher. See [this issue](https://github.com/Microsoft/TypeScript/issues/5285) for more details. @@ -430,6 +467,40 @@ grunt.initConfig({ }); ```` +#### allowUnreachableCode + +When set to true, TypeScript will not report errors on unreachable code. Requires TypeScript 1.8 or higher. + +````javascript +grunt.initConfig({ + ts: { + default: { + src: ["**/*.ts", "!node_modules/**"], + options: { + allowUnreachableCode: true + } + } + } +}); +```` + +#### allowUnusedLabels + +When set to true, TypeScript will not report errors when there are unused labels in your code. Requires TypeScript 1.8 or higher. + +````javascript +grunt.initConfig({ + ts: { + default: { + src: ["**/*.ts", "!node_modules/**"], + options: { + allowUnusedLabels: true + } + } + } +}); +```` + #### baseDir When using TypeScript >= 1.5 (most common), use [rootDir](#rootDir) instead. @@ -596,7 +667,7 @@ grunt.event.on('grunt-ts.failure', function() { true | false (default) ```` -Enable support for experimental ES7 async functionality. This is only available in TypeScript 1.6 and higher in 'es6' mode. +Enable support for experimental proposed ECMAScript async functionality. This is only available in TypeScript 1.6 and higher in 'es6' mode. ````javascript grunt.initConfig({ @@ -615,7 +686,7 @@ grunt.initConfig({ true | false (default) ```` -Enable support for experimental ES7 decorators. This is only available in TypeScript 1.5 and higher. +Enable support for experimental proposed ECMAScript decorators. This is only available in TypeScript 1.5 and higher. ````javascript grunt.initConfig({ @@ -672,6 +743,23 @@ grunt.initConfig({ }); ```` +#### forceConsistentCasingInFileNames + +When set to true, disallows inconsistently-cased references to the same file. For example, when using ES6-style imports, importing a file as "./MyLibrary" in one file and "./mylibrary" in another. + +````javascript +grunt.initConfig({ + ts: { + default: { + src: ["**/*.ts", "!node_modules/**"], + options: { + forceConsistentCasingInFileNames: true + } + } + } +}); +```` + #### htmlModuleTemplate Grunt-ts supports compilation of `.html` file content to TypeScript variables which is explained in detail [here](/docs/html2ts.md). The `htmlModuleTemplate` target property allows the developer to define a namespace for the templates. See also [html](#html) and [htmlVarTemplate](#htmlvartemplate). @@ -1007,6 +1095,28 @@ grunt.initConfig({ }); ```` +#### noFallthroughCasesInSwitch + +````javascript +true | false (default) +```` + +Report errors for fallthrough cases in switch statement. + +````javascript +grunt.initConfig({ + ts: { + default: { + src: ["**/*.ts", "!node_modules/**"], + options: { + noFallthroughCasesInSwitch: true + } + } + } +}); +```` + + #### noImplicitAny ````javascript @@ -1027,6 +1137,26 @@ grunt.initConfig({ }); ```` +#### noImplicitReturns + +````javascript +true | false (default) +```` + +Report error when not all code paths in function return a value. + +````javascript +grunt.initConfig({ + ts: { + default: { + options: { + noImplicitReturns: true + } + } + } +}); +```` + #### noLib ````javascript @@ -1085,6 +1215,44 @@ grunt.initConfig({ }); ```` +#### pretty + +````javascript +true | false (default) +```` + +Stylize errors and messages using color and context. + +````javascript +grunt.initConfig({ + ts: { + default: { + options: { + pretty: true + } + } + } +}); +```` + +#### reactNamespace + +````javascript +string +```` + +Specifies the object invoked for `createElement` and `__spread` when targeting 'react' JSX emit. Requires TypeScript 1.8 or higher and grunt-ts 5.5 or higher. + +````javascript +grunt.initConfig({ + ts: { + options: { + rootDir: "src/app" + } + } +}); +```` + #### removeComments ````javascript @@ -1121,6 +1289,26 @@ grunt.initConfig({ }); ```` +#### skipDefaultLibCheck + +````javascript +true | false (default) +```` + +Don't check a user-defined default lib file's validity. + +````javascript +grunt.initConfig({ + ts: { + default: { + options: { + skipDefaultLibCheck: true + } + } + } +}); +```` + #### sourceMap ````javascript diff --git a/package.json b/package.json index c6469dd3..8ca70010 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "basarat", "name": "grunt-ts", "description": "Compile and manage your TypeScript project", - "version": "5.5.0", + "version": "5.5.1", "homepage": "https://github.com/TypeStrong/grunt-ts", "repository": { "type": "git", diff --git a/tasks-internal/modules/amdLoader.js b/tasks-internal/modules/amdLoader.js index 2731415c..ef5d3849 100644 --- a/tasks-internal/modules/amdLoader.js +++ b/tasks-internal/modules/amdLoader.js @@ -1,4 +1,5 @@ /// +"use strict"; var _ = require('lodash'); var _str = require('underscore.string'); var path = require('path'); @@ -72,6 +73,21 @@ exports.getReferencesInOrder = getReferencesInOrder; // It updates based on the order of reference files function updateAmdLoader(referenceFile, files, loaderFile, loaderPath, outDir, newLine) { if (newLine === void 0) { newLine = utils.eol; } + var commonPath; + var makeRelativeToOutDir = function (files) { + files = _.map(files, function (file) { + // Remove common path and replace with absolute outDir + file = file.replace(commonPath, outDir); + // remove extension '.ts' / '.tsx': + file = file.substr(0, file.lastIndexOf('.')); + // Make relative to amd loader + file = utils.makeRelativePath(loaderPath, file); + // Prepend "./" to prevent "basePath" requirejs setting from interferring: + file = './' + file; + return file; + }); + return files; + }; // Read the original file if it exists if (fs.existsSync(referenceFile)) { grunt.log.verbose.writeln('Generating amdloader from reference file ' + referenceFile); @@ -105,25 +121,11 @@ function updateAmdLoader(referenceFile, files, loaderFile, loaderPath, outDir, n // Finally: outDir path + remainder section if (outDir) { // Find common path - var commonPath = utils.findCommonPath(files.before.concat(files.generated.concat(files.unordered.concat(files.after))), pathSeperator); + commonPath = utils.findCommonPath(files.before.concat(files.generated.concat(files.unordered.concat(files.after))), pathSeperator); grunt.log.verbose.writeln('Found common path: ' + commonPath); // Make sure outDir is absolute: outDir = path.resolve(outDir); grunt.log.verbose.writeln('Using outDir: ' + outDir); - function makeRelativeToOutDir(files) { - files = _.map(files, function (file) { - // Remove common path and replace with absolute outDir - file = file.replace(commonPath, outDir); - // remove ts extension '.ts': - file = file.substr(0, file.length - 3); - // Make relative to amd loader - file = utils.makeRelativePath(loaderPath, file); - // Prepend "./" to prevent "basePath" requirejs setting from interferring: - file = './' + file; - return file; - }); - return files; - } grunt.log.verbose.writeln('Making files relative to outDir...'); files.before = makeRelativeToOutDir(files.before); files.generated = makeRelativeToOutDir(files.generated); diff --git a/tasks-internal/modules/amdLoader.ts b/tasks-internal/modules/amdLoader.ts index 080ec2e9..6ba90652 100644 --- a/tasks-internal/modules/amdLoader.ts +++ b/tasks-internal/modules/amdLoader.ts @@ -110,6 +110,26 @@ export function getReferencesInOrder(referenceFile: string, referencePath: strin export function updateAmdLoader(referenceFile: string, files: IReferences, loaderFile: string, loaderPath: string, outDir: string, newLine = utils.eol) { + let commonPath: string; + const makeRelativeToOutDir = function(files: string[]) { + files = _.map(files, (file) => { + // Remove common path and replace with absolute outDir + file = file.replace(commonPath, outDir); + + // remove extension '.ts' / '.tsx': + file = file.substr(0, file.lastIndexOf('.')); + + // Make relative to amd loader + file = utils.makeRelativePath(loaderPath, file); + + // Prepend "./" to prevent "basePath" requirejs setting from interferring: + file = './' + file; + + return file; + }); + return files; + }; + // Read the original file if it exists if (fs.existsSync(referenceFile)) { grunt.log.verbose.writeln('Generating amdloader from reference file ' + referenceFile); @@ -145,32 +165,13 @@ export function updateAmdLoader(referenceFile: string, files: IReferences, loade // Finally: outDir path + remainder section if (outDir) { // Find common path - var commonPath = utils.findCommonPath(files.before.concat(files.generated.concat(files.unordered.concat(files.after))), pathSeperator); + commonPath = utils.findCommonPath(files.before.concat(files.generated.concat(files.unordered.concat(files.after))), pathSeperator); grunt.log.verbose.writeln('Found common path: ' + commonPath); // Make sure outDir is absolute: outDir = path.resolve(outDir); grunt.log.verbose.writeln('Using outDir: ' + outDir); - function makeRelativeToOutDir(files: string[]) { - files = _.map(files, (file) => { - // Remove common path and replace with absolute outDir - file = file.replace(commonPath, outDir); - - // remove ts extension '.ts': - file = file.substr(0, file.length - 3); - - // Make relative to amd loader - file = utils.makeRelativePath(loaderPath, file); - - // Prepend "./" to prevent "basePath" requirejs setting from interferring: - file = './' + file; - - return file; - }); - return files; - } - grunt.log.verbose.writeln('Making files relative to outDir...'); files.before = makeRelativeToOutDir(files.before); files.generated = makeRelativeToOutDir(files.generated); diff --git a/tasks-internal/modules/cacheUtils.js b/tasks-internal/modules/cacheUtils.js index 268b7ba2..3b8702cf 100644 --- a/tasks-internal/modules/cacheUtils.js +++ b/tasks-internal/modules/cacheUtils.js @@ -1,4 +1,5 @@ /// +"use strict"; // Source based on : https://github.com/tschaub/grunt-newer/blob/master/lib/util.js var fs = require('fs'); var _ = require('lodash'); diff --git a/tasks-internal/modules/compile.js b/tasks-internal/modules/compile.js index b68bb808..3baab9d7 100644 --- a/tasks-internal/modules/compile.js +++ b/tasks-internal/modules/compile.js @@ -6,6 +6,7 @@ var fs = require('fs'); var _ = require('lodash'); var utils = require('./utils'); var cache = require('./cacheUtils'); +var semver = require('semver'); var es6_promise_1 = require('es6-promise'); exports.grunt = require('grunt'); /////////////////////////// @@ -60,10 +61,13 @@ function resolveTypeScriptBinPath() { return path.join(ownRoot, binSub); } function getTsc(binPath) { - var pkg = JSON.parse(fs.readFileSync(path.resolve(binPath, '..', 'package.json')).toString()); - exports.grunt.log.writeln('Using tsc v' + pkg.version); return path.join(binPath, 'tsc'); } +function compileResultMeansFastCacheShouldBeRefreshed(options, result) { + return (options.fast !== 'never' && + (result.code === 0 || (result.code === 2 && !options.failOnTypeErrors))); +} +exports.compileResultMeansFastCacheShouldBeRefreshed = compileResultMeansFastCacheShouldBeRefreshed; function compileAllFiles(options, compilationInfo) { var targetFiles = compilationInfo.src; // Make a local copy so we can modify files without having external side effects @@ -123,11 +127,21 @@ function compileAllFiles(options, compilationInfo) { } // Quote the files to compile. Needed for command line parsing by tsc files = _.map(files, function (item) { return ("\"" + path.resolve(item) + "\""); }); - // if (outFile) { - // outFile = `"${path.resolve(outFile)}"`; - // } - var args = files.slice(0); + var args = files.slice(0), tsc, tscVersion = ''; var tsconfig = options.tsconfig; + if (options.compiler) { + // Custom compiler (task.compiler) + exports.grunt.log.writeln('Using the custom compiler : ' + options.compiler); + tsc = options.compiler; + tscVersion = ''; + } + else { + // the bundled OR npm module based compiler + var tscPath = resolveTypeScriptBinPath(); + tsc = getTsc(tscPath); + tscVersion = getTscVersion(tscPath); + exports.grunt.log.writeln('Using tsc v' + tscVersion); + } if (tsconfig && tsconfig.passThrough) { args.push('--project', tsconfig.tsconfig); } @@ -168,7 +182,7 @@ function compileAllFiles(options, compilationInfo) { if (options.inlineSourceMap) { args.push('--inlineSourceMap'); } - if (options.newLine && !utils.newLineIsRedundant(options.newLine)) { + if (options.newLine && !utils.newLineIsRedundantForTsc(options.newLine)) { args.push('--newLine', options.newLine); } if (options.isolatedModules) { @@ -192,14 +206,62 @@ function compileAllFiles(options, compilationInfo) { if (options.rootDir) { args.push('--rootDir', options.rootDir); } + if (options.noLib) { + args.push('--noLib'); + } + if (options.emitBOM) { + args.push('--emitBOM'); + } + if (options.locale) { + args.push('--locale', options.locale); + } + if (options.suppressExcessPropertyErrors) { + args.push('--suppressExcessPropertyErrors'); + } + if (options.stripInternal) { + args.push('--stripInternal'); + } + if (options.allowSyntheticDefaultImports) { + args.push('--allowSyntheticDefaultImports'); + } + if (options.reactNamespace) { + args.push('--reactNamespace', options.reactNamespace); + } + if (options.skipDefaultLibCheck) { + args.push('--skipDefaultLibCheck'); + } + if (options.pretty) { + args.push('--pretty'); + } + if (options.allowUnusedLabels) { + args.push('--allowUnusedLabels'); + } + if (options.noImplicitReturns) { + args.push('--noImplicitReturns'); + } + if (options.noFallthroughCasesInSwitch) { + args.push('--noFallthroughCasesInSwitch'); + } + if (options.allowUnreachableCode) { + args.push('--allowUnreachableCode'); + } + if (options.forceConsistentCasingInFileNames) { + args.push('--forceConsistentCasingInFileNames'); + } + if (options.allowJs) { + args.push('--allowJs'); + } + if (options.noImplicitUseStrict) { + args.push('--noImplicitUseStrict'); + } args.push('--target', options.target.toUpperCase()); if (options.module) { var moduleOptionString = ('' + options.module).toLowerCase(); - if ('amd|commonjs|system|umd'.indexOf(moduleOptionString) > -1) { + if ('amd|commonjs|system|umd|es6|es2015'.indexOf(moduleOptionString) > -1) { args.push('--module', moduleOptionString); } else { - console.warn('WARNING: Option "module" only supports "amd" | "commonjs" | "system" | "umd" '.magenta); + console.warn('WARNING: Option "module" only supports "amd" | "commonjs" | "system" | "umd" | "es6" | "es2015" '.magenta); } } if (compilationInfo.outDir) { @@ -209,6 +271,9 @@ function compileAllFiles(options, compilationInfo) { args.push('--outDir', compilationInfo.outDir); } if (compilationInfo.out) { + // We only pass --out instead of --outFile for backward-compatability reasons. + // It is the same for purposes of the command-line (the subtle difference is handled in the tsconfig code + // and the value of --outFile is copied to --out). args.push('--out', compilationInfo.out); } if (compilationInfo.dest && (!compilationInfo.out) && (!compilationInfo.outDir)) { @@ -238,9 +303,19 @@ function compileAllFiles(options, compilationInfo) { } } if (args.indexOf('--out') > -1 && args.indexOf('--module') > -1) { - console.warn(('WARNING: TypeScript does not allow external modules to be concatenated with' + - ' --out. Any exported code may be truncated. See TypeScript issue #1544 for' + - ' more details.').magenta); + if (semver.satisfies(tscVersion, '>=1.8.0')) { + if ((options.module === 'system' || options.module === 'amd')) { + } + else { + console.warn(('WARNING: TypeScript 1.8+ requires "module" to be set to' + + 'system or amd for concatenation of external modules to work.').magenta); + } + } + else { + console.warn(('WARNING: TypeScript < 1.8 does not allow external modules to be concatenated with' + + ' --out. Any exported code may be truncated. See TypeScript issue #1544 for' + + ' more details.').magenta); + } } if (options.sourceRoot) { args.push('--sourceRoot', options.sourceRoot); @@ -252,14 +327,9 @@ function compileAllFiles(options, compilationInfo) { if (options.additionalFlags) { args.push(options.additionalFlags); } - // Locate a compiler - var tsc; - if (options.compiler) { - exports.grunt.log.writeln('Using the custom compiler : ' + options.compiler); - tsc = options.compiler; - } - else { - tsc = getTsc(resolveTypeScriptBinPath()); + function getTscVersion(tscPath) { + var pkg = JSON.parse(fs.readFileSync(path.resolve(tscPath, '..', 'package.json')).toString()); + return '' + pkg.version; } // To debug the tsc command if (options.verbose) { @@ -295,7 +365,7 @@ function compileAllFiles(options, compilationInfo) { } // Execute command return executeNode(command, options).then(function (result) { - if (options.fast !== 'never' && result.code === 0) { + if (compileResultMeansFastCacheShouldBeRefreshed(options, result)) { resetChangedFiles(newFiles, options.targetName); } result.fileCount = files.length; diff --git a/tasks-internal/modules/compile.ts b/tasks-internal/modules/compile.ts index 99f7bef6..133bcbbd 100644 --- a/tasks-internal/modules/compile.ts +++ b/tasks-internal/modules/compile.ts @@ -3,13 +3,15 @@ 'use strict'; -import path = require('path'); -import fs = require('fs'); -import _ = require('lodash'); -import utils = require('./utils'); -import cache = require('./cacheUtils'); +import * as path from 'path'; +import * as fs from 'fs'; +import * as _ from 'lodash'; +import * as utils from './utils'; +import * as cache from './cacheUtils'; +import * as semver from 'semver'; import {Promise} from 'es6-promise'; + export var grunt: IGrunt = require('grunt'); /////////////////////////// @@ -77,12 +79,14 @@ function resolveTypeScriptBinPath(): string { } function getTsc(binPath: string): string { - var pkg = JSON.parse(fs.readFileSync(path.resolve(binPath, '..', 'package.json')).toString()); - grunt.log.writeln('Using tsc v' + pkg.version); - return path.join(binPath, 'tsc'); } +export function compileResultMeansFastCacheShouldBeRefreshed(options: IGruntTSOptions, result: ICompileResult) { + return (options.fast !== 'never' && + (result.code === 0 || (result.code === 2 && !options.failOnTypeErrors))); +} + export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGruntTSCompilationInfo): Promise { let targetFiles: string[] = compilationInfo.src; @@ -154,13 +158,24 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun // Quote the files to compile. Needed for command line parsing by tsc files = _.map(files, (item) => `"${path.resolve(item)}"`); - // if (outFile) { - // outFile = `"${path.resolve(outFile)}"`; - // } - - var args: string[] = files.slice(0); + let args: string[] = files.slice(0), + tsc: string, + tscVersion: string = ''; const tsconfig: ITSConfigSupport = options.tsconfig; + if (options.compiler) { + // Custom compiler (task.compiler) + grunt.log.writeln('Using the custom compiler : ' + options.compiler); + tsc = options.compiler; + tscVersion = ''; + } else { + // the bundled OR npm module based compiler + const tscPath = resolveTypeScriptBinPath(); + tsc = getTsc(tscPath); + tscVersion = getTscVersion(tscPath); + grunt.log.writeln('Using tsc v' + tscVersion); + } + if (tsconfig && tsconfig.passThrough) { args.push('--project', tsconfig.tsconfig); } else { @@ -200,7 +215,7 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun if (options.inlineSourceMap) { args.push('--inlineSourceMap'); } - if (options.newLine && !utils.newLineIsRedundant(options.newLine)) { + if (options.newLine && !utils.newLineIsRedundantForTsc(options.newLine)) { args.push('--newLine', options.newLine); } if (options.isolatedModules) { @@ -224,15 +239,63 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun if (options.rootDir) { args.push('--rootDir', options.rootDir); } + if (options.noLib) { + args.push('--noLib'); + } + if (options.emitBOM) { + args.push('--emitBOM'); + } + if (options.locale) { + args.push('--locale', options.locale); + } + if (options.suppressExcessPropertyErrors) { + args.push('--suppressExcessPropertyErrors'); + } + if (options.stripInternal) { + args.push('--stripInternal'); + } + if (options.allowSyntheticDefaultImports) { + args.push('--allowSyntheticDefaultImports'); + } + if (options.reactNamespace) { + args.push('--reactNamespace', options.reactNamespace); + } + if (options.skipDefaultLibCheck) { + args.push('--skipDefaultLibCheck'); + } + if (options.pretty) { + args.push('--pretty'); + } + if (options.allowUnusedLabels) { + args.push('--allowUnusedLabels'); + } + if (options.noImplicitReturns) { + args.push('--noImplicitReturns'); + } + if (options.noFallthroughCasesInSwitch) { + args.push('--noFallthroughCasesInSwitch'); + } + if (options.allowUnreachableCode) { + args.push('--allowUnreachableCode'); + } + if (options.forceConsistentCasingInFileNames) { + args.push('--forceConsistentCasingInFileNames'); + } + if (options.allowJs) { + args.push('--allowJs'); + } + if (options.noImplicitUseStrict) { + args.push('--noImplicitUseStrict'); + } args.push('--target', options.target.toUpperCase()); if (options.module) { let moduleOptionString: string = ('' + options.module).toLowerCase(); - if ('amd|commonjs|system|umd'.indexOf(moduleOptionString) > -1) { + if ('amd|commonjs|system|umd|es6|es2015'.indexOf(moduleOptionString) > -1) { args.push('--module', moduleOptionString); } else { - console.warn('WARNING: Option "module" only supports "amd" | "commonjs" | "system" | "umd" '.magenta); + console.warn('WARNING: Option "module" only supports "amd" | "commonjs" | "system" | "umd" | "es6" | "es2015" '.magenta); } } @@ -244,6 +307,9 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun } if (compilationInfo.out) { + // We only pass --out instead of --outFile for backward-compatability reasons. + // It is the same for purposes of the command-line (the subtle difference is handled in the tsconfig code + // and the value of --outFile is copied to --out). args.push('--out', compilationInfo.out); } @@ -273,9 +339,18 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun } if (args.indexOf('--out') > -1 && args.indexOf('--module') > -1) { - console.warn(('WARNING: TypeScript does not allow external modules to be concatenated with' + - ' --out. Any exported code may be truncated. See TypeScript issue #1544 for' + - ' more details.').magenta); + if (semver.satisfies(tscVersion, '>=1.8.0')) { + if ((options.module === 'system' || options.module === 'amd')) { + // this is fine. + } else { + console.warn(('WARNING: TypeScript 1.8+ requires "module" to be set to' + + 'system or amd for concatenation of external modules to work.').magenta); + } + } else { + console.warn(('WARNING: TypeScript < 1.8 does not allow external modules to be concatenated with' + + ' --out. Any exported code may be truncated. See TypeScript issue #1544 for' + + ' more details.').magenta); + } } if (options.sourceRoot) { @@ -290,13 +365,9 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun args.push(options.additionalFlags); } - // Locate a compiler - let tsc: string; - if (options.compiler) { // Custom compiler (task.compiler) - grunt.log.writeln('Using the custom compiler : ' + options.compiler); - tsc = options.compiler; - } else { // the bundled OR npm module based compiler - tsc = getTsc(resolveTypeScriptBinPath()); + function getTscVersion(tscPath: string) { + const pkg = JSON.parse(fs.readFileSync(path.resolve(tscPath, '..', 'package.json')).toString()); + return '' + pkg.version; } // To debug the tsc command @@ -337,7 +408,7 @@ export function compileAllFiles(options: IGruntTSOptions, compilationInfo: IGrun // Execute command return executeNode(command, options).then((result: ICompileResult) => { - if (options.fast !== 'never' && result.code === 0) { + if (compileResultMeansFastCacheShouldBeRefreshed(options, result)) { resetChangedFiles(newFiles, options.targetName); } diff --git a/tasks-internal/modules/defaults.js b/tasks-internal/modules/defaults.js index f83ec4ae..d286a9ce 100644 --- a/tasks-internal/modules/defaults.js +++ b/tasks-internal/modules/defaults.js @@ -1,8 +1,10 @@ 'use strict'; var utils = require('./utils'); -exports.TypeScriptDefaults = { +var _ = require('lodash'); +var TypeScriptDefaults = { allowBool: false, allowImportModule: false, + allowSyntheticDefaultImports: null, amdloader: null, compile: true, declaration: false, @@ -27,11 +29,16 @@ exports.TypeScriptDefaults = { htmlOutputTemplate: null, htmlOutDir: null, htmlOutDirFlatten: null, - failOnTypeErrors: true, + failOnTypeErrors: null, + emitGruntEvents: null, noEmitOnError: false, preserveConstEnums: false, + suppressExcessPropertyErrors: false, suppressImplicitAnyIndexErrors: false, + stripInternal: false, noEmit: false, + noLib: false, + emitBOM: false, inlineSources: false, inlineSourceMap: false, newLine: utils.eol, @@ -40,15 +47,27 @@ exports.TypeScriptDefaults = { additionalFlags: '', templateCache: null, targetName: '', + locale: null, jsx: null, moduleResolution: null, experimentalAsyncFunctions: null, + reactNamespace: null, + skipDefaultLibCheck: null, + pretty: false, + allowUnusedLabels: false, + noImplicitReturns: false, + noFallthroughCasesInSwitch: false, + allowUnreachableCode: false, + forceConsistentCasingInFileNames: false, + allowJs: false, + noImplicitUseStrict: false, rootDir: null, warnings: [], errors: [] }; -exports.GruntTSDefaults = applyGruntTSDefaults(exports.TypeScriptDefaults); +exports.GruntTSDefaults = applyGruntTSDefaults(_.clone(TypeScriptDefaults)); function applyGruntTSDefaults(options) { + // this function applies defaults where grunt-ts differs from TypeScript options.sourceMap = true; options.target = 'es5'; options.htmlModuleTemplate = '<%= filename %>'; @@ -56,6 +75,8 @@ function applyGruntTSDefaults(options) { options.htmlOutDirFlatten = false; options.fast = 'watch'; options.removeComments = true; + options.failOnTypeErrors = true; + options.emitGruntEvents = false; return options; } //# sourceMappingURL=defaults.js.map \ No newline at end of file diff --git a/tasks-internal/modules/defaults.ts b/tasks-internal/modules/defaults.ts index e26c73c4..31625f27 100644 --- a/tasks-internal/modules/defaults.ts +++ b/tasks-internal/modules/defaults.ts @@ -1,10 +1,12 @@ 'use strict'; import * as utils from './utils'; +import * as _ from 'lodash'; -export const TypeScriptDefaults: IGruntTSOptions = { +const TypeScriptDefaults: IGruntTSOptions = { allowBool: false, allowImportModule: false, + allowSyntheticDefaultImports: null, amdloader: null, compile: true, declaration: false, @@ -29,11 +31,16 @@ export const TypeScriptDefaults: IGruntTSOptions = { htmlOutputTemplate: null, htmlOutDir: null, htmlOutDirFlatten: null, - failOnTypeErrors: true, + failOnTypeErrors: null, + emitGruntEvents: null, noEmitOnError: false, preserveConstEnums: false, + suppressExcessPropertyErrors: false, suppressImplicitAnyIndexErrors: false, + stripInternal: false, noEmit: false, + noLib: false, + emitBOM: false, inlineSources: false, inlineSourceMap: false, newLine: utils.eol, @@ -42,18 +49,30 @@ export const TypeScriptDefaults: IGruntTSOptions = { additionalFlags: '', templateCache: null, targetName: '', + locale: null, jsx: null, moduleResolution: null, experimentalAsyncFunctions: null, + reactNamespace: null, + skipDefaultLibCheck: null, + pretty: false, + allowUnusedLabels: false, + noImplicitReturns: false, + noFallthroughCasesInSwitch: false, + allowUnreachableCode: false, + forceConsistentCasingInFileNames: false, + allowJs: false, + noImplicitUseStrict: false, rootDir: null, warnings: [], errors: [] }; -export const GruntTSDefaults = applyGruntTSDefaults(TypeScriptDefaults); +export const GruntTSDefaults = applyGruntTSDefaults(_.clone(TypeScriptDefaults)); function applyGruntTSDefaults(options: IGruntTSOptions) { + // this function applies defaults where grunt-ts differs from TypeScript options.sourceMap = true; options.target = 'es5'; options.htmlModuleTemplate = '<%= filename %>'; @@ -61,5 +80,7 @@ function applyGruntTSDefaults(options: IGruntTSOptions) { options.htmlOutDirFlatten = false; options.fast = 'watch'; options.removeComments = true; + options.failOnTypeErrors = true; + options.emitGruntEvents = false; return options; } diff --git a/tasks-internal/modules/html2ts.js b/tasks-internal/modules/html2ts.js index b7edfc98..5dd7c8db 100644 --- a/tasks-internal/modules/html2ts.js +++ b/tasks-internal/modules/html2ts.js @@ -1,4 +1,5 @@ /// +"use strict"; var _ = require('lodash'); var fs = require('fs'); var path = require('path'); @@ -18,6 +19,12 @@ var escapeContent = function (content, quoteChar) { var nlReplace = ''; return content.replace(quoteRegexp, '\\' + quoteChar).replace(/\r?\n/g, nlReplace); }; +// Convert a string to camelCase +// Inspired by http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ +// Solves the issue of serving a module name that includes dashes +var toCamel = function (str) { + return str.replace(/(\-[a-z])/g, function ($1) { return $1.toUpperCase().replace('-', ''); }); +}; // Remove bom when reading utf8 files function stripBOM(str) { return 0xFEFF === str.charCodeAt(0) @@ -40,8 +47,8 @@ function compileHTML(filename, options) { // TODO: place a minification pipeline here if you want. var ext = path.extname(filename).replace('.', ''); var extFreename = path.basename(filename, '.' + ext); - var moduleName = options.moduleFunction({ ext: ext, filename: extFreename }); - var varName = options.varFunction({ ext: ext, filename: extFreename }).replace(/\./g, '_'); + var moduleName = toCamel(options.moduleFunction({ ext: ext, filename: extFreename })); + var varName = toCamel(options.varFunction({ ext: ext, filename: extFreename }).replace(/\./g, '_')); var fileContent; if (!options.htmlOutputTemplate) { fileContent = _.template(htmlInternalTemplate(options.eol))({ modulename: moduleName, varname: varName, content: htmlContent }); diff --git a/tasks-internal/modules/html2ts.ts b/tasks-internal/modules/html2ts.ts index 024ef858..f7411b30 100644 --- a/tasks-internal/modules/html2ts.ts +++ b/tasks-internal/modules/html2ts.ts @@ -22,6 +22,13 @@ var escapeContent = function (content: string, quoteChar= '\''): string { return content.replace(quoteRegexp, '\\' + quoteChar).replace(/\r?\n/g, nlReplace); }; +// Convert a string to camelCase +// Inspired by http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ +// Solves the issue of serving a module name that includes dashes +var toCamel = function(str){ + return str.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-', ''); }); +}; + // Remove bom when reading utf8 files function stripBOM(str) { return 0xFEFF === str.charCodeAt(0) @@ -60,8 +67,8 @@ export function compileHTML(filename: string, options: IHtml2TSOptions): string var ext = path.extname(filename).replace('.', ''); var extFreename = path.basename(filename, '.' + ext); - var moduleName = options.moduleFunction({ ext: ext, filename: extFreename }); - var varName = options.varFunction({ ext: ext, filename: extFreename }).replace(/\./g, '_'); + var moduleName = toCamel(options.moduleFunction({ ext: ext, filename: extFreename })); + var varName = toCamel(options.varFunction({ ext: ext, filename: extFreename }).replace(/\./g, '_')); var fileContent; if (!options.htmlOutputTemplate) { diff --git a/tasks-internal/modules/interfaces.d.ts b/tasks-internal/modules/interfaces.d.ts index f5e81f0e..c5e66de6 100644 --- a/tasks-internal/modules/interfaces.d.ts +++ b/tasks-internal/modules/interfaces.d.ts @@ -34,7 +34,7 @@ interface ITaskOptions { emitDecoratorMetadata: boolean; experimentalDecorators: boolean; mapRoot: string; - /** amd | commonjs | umd | system */ + /** amd | commonjs | umd | system | es6 | es2015 */ module: string; noImplicitAny: boolean; noResolve: boolean; @@ -81,6 +81,40 @@ interface ITaskOptions { experimentalAsyncFunctions: string; /** Sepecifies the root directory of input files. Use to control the output directory structure with --outDir. */ rootDir: string; + /** grunt-ts setting to emit events in Grunt */ + emitGruntEvents: boolean; + /** noLib - do not auto-include the lib.d.ts file in the compilation context */ + noLib: boolean; + /** emitBOM - indicates if emitted files should include a Byte Order Mark */ + emitBOM: boolean; + /** locale - pass a culture string like "en" or "ja-jp" for locale-specific error messages (requires error file in same folder as tsc) */ + locale: string; + /** Disables strict object literal assignment checking */ + suppressExcessPropertyErrors: boolean; + /** Does not emit objects marked as internal */ + stripInternal: boolean; + /** Assumes a defalt export as the whole module if one is not specified, or as the only export if only one export is specified */ + allowSyntheticDefaultImports: boolean; + /** Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit. */ + reactNamespace: string; + /** Treat a file as a default lib if it has '/// at the top */ + skipDefaultLibCheck: boolean; + /** Stylize errors and messages using color and context. */ + pretty: boolean; + /** Do not report errors on unused labels. */ + allowUnusedLabels: boolean; + /** Report error when not all code paths in function return a value. */ + noImplicitReturns: boolean; + /** Report errors for fallthrough cases in switch statement. */ + noFallthroughCasesInSwitch: boolean; + /** Do not report errors on unreachable code. */ + allowUnreachableCode: boolean; + /** Disallow inconsistently-cased references to the same file. */ + forceConsistentCasingInFileNames: boolean; + /** Allow JavaScript files to be compiled. */ + allowJs: boolean; + /** Do not emit "use strict" directives in module output. */ + noImplicitUseStrict: boolean; } interface IVisualStudioProjectSupport { @@ -110,6 +144,7 @@ interface IGruntTSCompilationInfo extends grunt.file.IFilesConfig { outDir?: string; out?: string; src?: string[]; + glob?: string[]; } declare module 'strip-bom' { diff --git a/tasks-internal/modules/optionsResolver.js b/tasks-internal/modules/optionsResolver.js index 6db1745f..6d87293e 100644 --- a/tasks-internal/modules/optionsResolver.js +++ b/tasks-internal/modules/optionsResolver.js @@ -7,13 +7,20 @@ var _ = require('lodash'); var es6_promise_1 = require('es6-promise'); var visualStudioOptionsResolver_1 = require('./visualStudioOptionsResolver'); var tsconfig_1 = require('./tsconfig'); -var propertiesFromTarget = ['amdloader', 'html', 'htmlOutDir', 'htmlOutDirFlatten', 'reference', 'testExecute', 'tsconfig', - 'templateCache', 'vs', 'watch'], propertiesFromTargetOptions = ['additionalFlags', 'comments', 'compile', 'compiler', 'declaration', - 'emitDecoratorMetadata', 'experimentalDecorators', 'failOnTypeErrors', 'fast', 'htmlModuleTemplate', 'htmlOutDir', - 'htmlOutputTemplate', 'htmlOutDirFlatten', 'htmlVarTemplate', 'inlineSourceMap', 'inlineSources', 'isolatedModules', - 'mapRoot', 'module', 'newLine', 'noEmit', 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noResolve', - 'preserveConstEnums', 'removeComments', 'sourceRoot', 'sourceMap', 'suppressImplicitAnyIndexErrors', 'target', - 'verbose', 'jsx', 'moduleResolution', 'experimentalAsyncFunctions', 'rootDir'], delayTemplateExpansion = ['htmlModuleTemplate', 'htmlVarTemplate','htmlOutputTemplate']; +// Compiler Options documentation: +// https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Compiler%20Options.md +var propertiesFromTarget = ['amdloader', 'baseDir', 'html', 'htmlOutDir', 'htmlOutDirFlatten', 'reference', 'testExecute', 'tsconfig', + 'templateCache', 'vs', 'watch'], +// purposefully not supported: help, version, charset, diagnostics, listFiles +// supported via other code: out, outDir, outFile, project +propertiesFromTargetOptions = ['additionalFlags', 'allowSyntheticDefaultImports', 'comments', 'compile', 'compiler', 'declaration', + 'emitBOM', 'emitDecoratorMetadata', 'experimentalDecorators', 'failOnTypeErrors', 'fast', 'htmlModuleTemplate', 'htmlOutDir', + 'htmlOutputTemplate', 'htmlOutDirFlatten', 'htmlVarTemplate', 'inlineSourceMap', 'inlineSources', 'isolatedModules', 'locale', + 'mapRoot', 'module', 'newLine', 'noEmit', 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noLib', 'noResolve', + 'preserveConstEnums', 'removeComments', 'sourceRoot', 'sourceMap', 'stripInternal', 'suppressExcessPropertyErrors', + 'suppressImplicitAnyIndexErrors', 'target', 'verbose', 'jsx', 'moduleResolution', 'experimentalAsyncFunctions', 'rootDir', + 'emitGruntEvents', 'reactNamespace', 'skipDefaultLibCheck', 'pretty', 'allowUnusedLabels', 'noImplicitReturns', + 'noFallthroughCasesInSwitch', 'allowUnreachableCode', 'forceConsistentCasingInFileNames', 'allowJs', 'noImplicitUseStrict'], delayTemplateExpansion = ['htmlModuleTemplate', 'htmlVarTemplate', 'htmlOutputTemplate']; var templateProcessor = null; var globExpander = null; function noopTemplateProcessor(templateString, options) { @@ -23,11 +30,12 @@ function emptyGlobExpander(globs) { return []; } emptyGlobExpander.isStub = true; -function resolveAsync(rawTaskOptions, rawTargetOptions, targetName, files, theTemplateProcessor, theGlobExpander) { +function resolveAsync(rawTaskOptions, rawTargetOptions, targetName, resolvedFiles, theTemplateProcessor, theGlobExpander) { if (targetName === void 0) { targetName = ''; } - if (files === void 0) { files = []; } + if (resolvedFiles === void 0) { resolvedFiles = []; } if (theTemplateProcessor === void 0) { theTemplateProcessor = null; } if (theGlobExpander === void 0) { theGlobExpander = null; } + var result = emptyOptionsResolveResult(); return new es6_promise_1.Promise(function (resolve, reject) { if (theTemplateProcessor && typeof theTemplateProcessor === 'function') { templateProcessor = theTemplateProcessor; @@ -43,7 +51,6 @@ function resolveAsync(rawTaskOptions, rawTargetOptions, targetName, files, theTe } fixMissingOptions(rawTaskOptions); fixMissingOptions(rawTargetOptions); - var result = emptyOptionsResolveResult(); { var _a = resolveAndWarnOnConfigurationIssues(rawTaskOptions, rawTargetOptions, targetName), errors = _a.errors, warnings = _a.warnings; (_b = result.errors).push.apply(_b, errors); @@ -51,7 +58,7 @@ function resolveAsync(rawTaskOptions, rawTargetOptions, targetName, files, theTe } result = applyGruntOptions(result, rawTaskOptions); result = applyGruntOptions(result, rawTargetOptions); - result = copyCompilationTasks(result, files); + result = copyCompilationTasks(result, resolvedFiles, resolveOutputOptions(rawTaskOptions, rawTargetOptions)); visualStudioOptionsResolver_1.resolveVSOptionsAsync(result, rawTaskOptions, rawTargetOptions, templateProcessor).then(function (result) { tsconfig_1.resolveAsync(result, rawTaskOptions, rawTargetOptions, templateProcessor, globExpander).then(function (result) { result = addressAssociatedOptionsAndResolveConflicts(result); @@ -63,16 +70,31 @@ function resolveAsync(rawTaskOptions, rawTargetOptions, targetName, files, theTe result.targetName = targetName; } return resolve(result); - }).catch(function (error) { - return reject(error); + }).catch(function (tsConfigError) { + result.errors.push('tsconfig error: ' + JSON.stringify(tsConfigError)); + return resolve(result); }); - }).catch(function (error) { - return reject(error); + }).catch(function (vsConfigError) { + result.errors.push('Visual Studio config issue: ' + JSON.stringify(vsConfigError)); + return resolve(result); }); var _b, _c; }); } exports.resolveAsync = resolveAsync; +function resolveOutputOptions(rawTaskOptions, rawTargetOptions) { + var result = {}; + var props = ['outDir', 'out']; + var options = [rawTaskOptions, rawTargetOptions]; + options.forEach(function (opt) { + props.forEach(function (prop) { + if (opt && (prop in opt)) { + result[prop] = opt[prop]; + } + }); + }); + return result; +} function fixMissingOptions(config) { if (config && !config.options) { config.options = {}; @@ -101,7 +123,11 @@ function resolveAndWarnOnConfigurationIssues(task, target, targetName) { return { errors: errors, warnings: warnings }; function getAdditionalWarnings(task, target, targetName) { var additionalWarnings = []; - if (((task && task.src) || (target && target.src)) && + if (propertiesFromTarget.indexOf(targetName) >= 0) { + additionalWarnings.push(("Warning: Using the grunt-ts keyword \"" + targetName + "\" as a target name may cause ") + + "incorrect behavior or errors."); + } + if (((task && task.src && targetName !== 'src') || (target && target.src)) && ((task && task.files) || (target && target.files))) { additionalWarnings.push("Warning: In task \"" + targetName + "\", either \"files\" or \"src\" should be used - not both."); } @@ -113,6 +139,10 @@ function resolveAndWarnOnConfigurationIssues(task, target, targetName) { additionalWarnings.push(("Warning: target \"" + targetName + "\" has an array specified for the files.dest property.") + " This is not supported. Taking first element and ignoring the rest."); } + if ((task && task.outFile) || (target && target.outFile)) { + additionalWarnings.push(("Warning: target \"" + targetName + "\" is using \"outFile\". This is not supported by") + + " grunt-ts via the Gruntfile - it's only relevant when present in tsconfig.json file. Use \"out\" instead."); + } return additionalWarnings; function usingDestArray(task) { var result = false; @@ -208,8 +238,8 @@ function resolveAndWarnOnConfigurationIssues(task, target, targetName) { } function applyGruntOptions(applyTo, gruntOptions) { if (gruntOptions) { - for (var _i = 0; _i < propertiesFromTarget.length; _i++) { - var propertyName = propertiesFromTarget[_i]; + for (var _i = 0, propertiesFromTarget_1 = propertiesFromTarget; _i < propertiesFromTarget_1.length; _i++) { + var propertyName = propertiesFromTarget_1[_i]; if (propertyName in gruntOptions && propertyName !== 'vs') { if (typeof gruntOptions[propertyName] === 'string' && utils.hasValue(gruntOptions[propertyName]) && delayTemplateExpansion.indexOf(propertyName) === -1) { @@ -221,8 +251,8 @@ function applyGruntOptions(applyTo, gruntOptions) { } } if (gruntOptions.options) { - for (var _a = 0; _a < propertiesFromTargetOptions.length; _a++) { - var propertyName = propertiesFromTargetOptions[_a]; + for (var _a = 0, propertiesFromTargetOptions_1 = propertiesFromTargetOptions; _a < propertiesFromTargetOptions_1.length; _a++) { + var propertyName = propertiesFromTargetOptions_1[_a]; if (propertyName in gruntOptions.options) { if (typeof gruntOptions.options[propertyName] === 'string' && utils.hasValue(gruntOptions.options[propertyName]) && delayTemplateExpansion.indexOf(propertyName) === -1) { @@ -237,27 +267,45 @@ function applyGruntOptions(applyTo, gruntOptions) { } return applyTo; } -function copyCompilationTasks(options, files) { +function copyCompilationTasks(options, resolvedFiles, outputInfo) { if (!utils.hasValue(options.CompilationTasks)) { options.CompilationTasks = []; } - if (!utils.hasValue(files)) { + if (!utils.hasValue(resolvedFiles) || resolvedFiles.length === 0) { + if (options.CompilationTasks.length === 0 && (('outDir' in outputInfo) || ('out' in outputInfo))) { + var newCompilationTask = { + src: [] + }; + if ('outDir' in outputInfo) { + newCompilationTask.outDir = outputInfo.outDir; + } + if ('out' in outputInfo) { + newCompilationTask.outDir = outputInfo.outDir; + } + options.CompilationTasks.push(newCompilationTask); + } return options; } - for (var i = 0; i < files.length; i += 1) { + for (var i = 0; i < resolvedFiles.length; i += 1) { + var glob = void 0; + var orig = resolvedFiles[i].orig; + if (orig && ('src' in orig)) { + glob = [].concat(orig.src); + } var compilationSet = { - src: _.map(files[i].src, function (fileName) { return utils.enclosePathInQuotesIfRequired(fileName); }), - out: utils.enclosePathInQuotesIfRequired(files[i].out), - outDir: utils.enclosePathInQuotesIfRequired(files[i].outDir) + src: _.map(resolvedFiles[i].src, function (fileName) { return utils.enclosePathInQuotesIfRequired(fileName); }), + out: utils.enclosePathInQuotesIfRequired(resolvedFiles[i].out), + outDir: utils.enclosePathInQuotesIfRequired(resolvedFiles[i].outDir), + glob: glob }; - if ('dest' in files[i] && files[i].dest) { + if ('dest' in resolvedFiles[i] && resolvedFiles[i].dest) { var dest = void 0; - if (_.isArray(files[i].dest)) { + if (_.isArray(resolvedFiles[i].dest)) { // using an array for dest is not supported. Only take first element. - dest = files[i].dest[0]; + dest = resolvedFiles[i].dest[0]; } else { - dest = files[i].dest; + dest = resolvedFiles[i].dest; } if (utils.isJavaScriptFile(dest)) { compilationSet.out = dest; @@ -290,9 +338,6 @@ function addressAssociatedOptionsAndResolveConflicts(options) { options.warnings.push('TypeScript cannot use inlineSourceMap and sourceMap together. Ignoring sourceMap.'); options.sourceMap = false; } - if (options.inlineSources && options.sourceMap) { - options.errors.push('It is not permitted to use inlineSources and sourceMap together. Use one or the other.'); - } if (options.inlineSources && !options.sourceMap) { options.inlineSources = true; options.inlineSourceMap = true; @@ -310,13 +355,14 @@ function addressAssociatedOptionsAndResolveConflicts(options) { options.removeComments = !!options.removeComments; options.comments = !options.removeComments; } - if ('html' in options && options.CompilationTasks.length === 0) { - options.errors.push("ERROR: option `html` provided without specifying corresponding TypeScript source files to " + - "compile. The transform will not occur unless grunt-ts also expects to compile these files."); + if ('html' in options && + (options.CompilationTasks.length === 0 || + !_.some(options.CompilationTasks, function (item) { return ((item.src || []).length > 0 || (item.glob || []).length > 0); }))) { + options.errors.push("ERROR: option \"html\" provided without corresponding TypeScript source files or glob to " + + "compile. The transform will not occur unless grunt-ts also expects to compile some files."); } options.CompilationTasks.forEach(function (compileTask) { if (compileTask.out && compileTask.outDir) { - console.log(JSON.stringify(compileTask)); options.warnings.push('The parameter `out` is incompatible with `outDir`; pass one or the other - not both. Ignoring `out` and using `outDir`.'); compileTask.out = ''; } @@ -351,6 +397,12 @@ function applyGruntTSDefaults(options) { if (!('removeComments' in options) && !('comments' in options)) { options.removeComments = defaults_1.GruntTSDefaults.removeComments; } + if (!('failOnTypeErrors' in options)) { + options.failOnTypeErrors = defaults_1.GruntTSDefaults.failOnTypeErrors; + } + if (!('emitGruntEvents' in options)) { + options.emitGruntEvents = defaults_1.GruntTSDefaults.emitGruntEvents; + } return options; } //# sourceMappingURL=optionsResolver.js.map \ No newline at end of file diff --git a/tasks-internal/modules/optionsResolver.ts b/tasks-internal/modules/optionsResolver.ts index 78e64fc2..a56d0315 100644 --- a/tasks-internal/modules/optionsResolver.ts +++ b/tasks-internal/modules/optionsResolver.ts @@ -10,15 +10,22 @@ import {Promise} from 'es6-promise'; import {resolveVSOptionsAsync} from './visualStudioOptionsResolver'; import {resolveAsync as resolveTSConfigAsync} from './tsconfig'; -const propertiesFromTarget = ['amdloader', 'html', 'htmlOutDir', 'htmlOutDirFlatten', 'reference', 'testExecute', 'tsconfig', +// Compiler Options documentation: +// https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Compiler%20Options.md + +const propertiesFromTarget = ['amdloader', 'baseDir', 'html', 'htmlOutDir', 'htmlOutDirFlatten', 'reference', 'testExecute', 'tsconfig', 'templateCache', 'vs', 'watch'], - propertiesFromTargetOptions = ['additionalFlags', 'comments', 'compile', 'compiler', 'declaration', - 'emitDecoratorMetadata', 'experimentalDecorators', 'failOnTypeErrors', 'fast', 'htmlModuleTemplate', 'htmlOutDir', - 'htmlOutputTemplate', 'htmlOutDirFlatten', 'htmlVarTemplate', 'inlineSourceMap', 'inlineSources', 'isolatedModules', - 'mapRoot', 'module', 'newLine', 'noEmit', 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noResolve', - 'preserveConstEnums', 'removeComments', 'sourceRoot', 'sourceMap', 'suppressImplicitAnyIndexErrors', 'target', - 'verbose', 'jsx', 'moduleResolution', 'experimentalAsyncFunctions', 'rootDir'], - delayTemplateExpansion = ['htmlModuleTemplate', 'htmlVarTemplate','htmlOutputTemplate']; + // purposefully not supported: help, version, charset, diagnostics, listFiles + // supported via other code: out, outDir, outFile, project + propertiesFromTargetOptions = ['additionalFlags', 'allowSyntheticDefaultImports', 'comments', 'compile', 'compiler', 'declaration', + 'emitBOM', 'emitDecoratorMetadata', 'experimentalDecorators', 'failOnTypeErrors', 'fast', 'htmlModuleTemplate', 'htmlOutDir', + 'htmlOutputTemplate', 'htmlOutDirFlatten', 'htmlVarTemplate', 'inlineSourceMap', 'inlineSources', 'isolatedModules', 'locale', + 'mapRoot', 'module', 'newLine', 'noEmit', 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noLib', 'noResolve', + 'preserveConstEnums', 'removeComments', 'sourceRoot', 'sourceMap', 'stripInternal', 'suppressExcessPropertyErrors', + 'suppressImplicitAnyIndexErrors', 'target', 'verbose', 'jsx', 'moduleResolution', 'experimentalAsyncFunctions', 'rootDir', + 'emitGruntEvents', 'reactNamespace', 'skipDefaultLibCheck', 'pretty', 'allowUnusedLabels', 'noImplicitReturns', + 'noFallthroughCasesInSwitch', 'allowUnreachableCode', 'forceConsistentCasingInFileNames', 'allowJs', 'noImplicitUseStrict'], + delayTemplateExpansion = ['htmlModuleTemplate', 'htmlVarTemplate', 'htmlOutputTemplate']; let templateProcessor: (templateString: string, options: any) => string = null; let globExpander: (globs: string[]) => string[] = null; @@ -36,10 +43,11 @@ function emptyGlobExpander(globs: string[]): string[] { export function resolveAsync(rawTaskOptions: ITargetOptions, rawTargetOptions: ITargetOptions, targetName = '', - files: IGruntTSCompilationInfo[] = [], + resolvedFiles: IGruntTSCompilationInfo[] = [], theTemplateProcessor: (templateString: string, options: any) => string = null, theGlobExpander: (globs: string[]) => string[] = null): Promise { + let result = emptyOptionsResolveResult(); return new Promise((resolve, reject) => { @@ -58,7 +66,6 @@ export function resolveAsync(rawTaskOptions: ITargetOptions, fixMissingOptions(rawTaskOptions); fixMissingOptions(rawTargetOptions); - let result = emptyOptionsResolveResult(); { const {errors, warnings} = resolveAndWarnOnConfigurationIssues(rawTaskOptions, rawTargetOptions, targetName); result.errors.push(...errors); @@ -66,7 +73,7 @@ export function resolveAsync(rawTaskOptions: ITargetOptions, } result = applyGruntOptions(result, rawTaskOptions); result = applyGruntOptions(result, rawTargetOptions); - result = copyCompilationTasks(result, files); + result = copyCompilationTasks(result, resolvedFiles, resolveOutputOptions(rawTaskOptions, rawTargetOptions)); resolveVSOptionsAsync(result, rawTaskOptions, rawTargetOptions, templateProcessor).then((result) => { resolveTSConfigAsync(result, rawTaskOptions, rawTargetOptions, templateProcessor, globExpander).then((result) => { @@ -82,15 +89,35 @@ export function resolveAsync(rawTaskOptions: ITargetOptions, } return resolve(result); - }).catch((error) => { - return reject(error); + }).catch((tsConfigError) => { + result.errors.push('tsconfig error: ' + JSON.stringify(tsConfigError)); + return resolve(result); }); - }).catch((error) => { - return reject(error); + }).catch((vsConfigError) => { + result.errors.push('Visual Studio config issue: ' + JSON.stringify(vsConfigError)); + return resolve(result); }); }); } +function resolveOutputOptions(rawTaskOptions: + IGruntTargetOptions, rawTargetOptions: IGruntTargetOptions) { + const result: {outDir?: string, out?: string} = {}; + + const props = ['outDir', 'out']; + const options = [rawTaskOptions, rawTargetOptions]; + + options.forEach((opt) => { + props.forEach((prop) => { + if (opt && (prop in opt)) { + result[prop] = opt[prop]; + } + }); + }); + + return result; +} + function fixMissingOptions(config: ITargetOptions) { if (config && !config.options) { config.options = {}; @@ -129,7 +156,13 @@ function resolveAndWarnOnConfigurationIssues(task: ITargetOptions, function getAdditionalWarnings(task: any, target: any, targetName: string) { const additionalWarnings = []; - if (((task && task.src) || (target && target.src)) && + + if (propertiesFromTarget.indexOf(targetName) >= 0) { + additionalWarnings.push(`Warning: Using the grunt-ts keyword "${targetName}" as a target name may cause ` + + `incorrect behavior or errors.`); + } + + if (((task && task.src && targetName !== 'src') || (target && target.src)) && ((task && task.files) || (target && target.files))) { additionalWarnings.push(`Warning: In task "${targetName}", either "files" or "src" should be used - not both.`); } @@ -139,12 +172,16 @@ function resolveAndWarnOnConfigurationIssues(task: ITargetOptions, additionalWarnings.push(`Warning: In task "${targetName}", either "files" or "vs" should be used - not both.`); } - if (usingDestArray(task) || usingDestArray(target)) { additionalWarnings.push(`Warning: target "${targetName}" has an array specified for the files.dest property.` + ` This is not supported. Taking first element and ignoring the rest.`); } + if ((task && task.outFile) || (target && target.outFile)) { + additionalWarnings.push(`Warning: target "${targetName}" is using "outFile". This is not supported by` + + ` grunt-ts via the Gruntfile - it's only relevant when present in tsconfig.json file. Use "out" instead.`); + } + return additionalWarnings; function usingDestArray(task) { @@ -283,27 +320,46 @@ function applyGruntOptions(applyTo: IGruntTSOptions, gruntOptions: ITargetOption return applyTo; } -function copyCompilationTasks(options: IGruntTSOptions, files: IGruntTSCompilationInfo[]) { +function copyCompilationTasks(options: IGruntTSOptions, resolvedFiles: IGruntTSCompilationInfo[], outputInfo: {outDir?: string, out?: string}) { if (!utils.hasValue(options.CompilationTasks)) { options.CompilationTasks = []; } - if (!utils.hasValue(files)) { + if (!utils.hasValue(resolvedFiles) || resolvedFiles.length === 0) { + if (options.CompilationTasks.length === 0 && (('outDir' in outputInfo) || ('out' in outputInfo))) { + const newCompilationTask : IGruntTSCompilationInfo = { + src: [] + }; + if ('outDir' in outputInfo) { + newCompilationTask.outDir = outputInfo.outDir; + } + if ('out' in outputInfo) { + newCompilationTask.outDir = outputInfo.outDir; + } + options.CompilationTasks.push(newCompilationTask); + } return options; } - for (let i = 0; i < files.length; i += 1) { + for (let i = 0; i < resolvedFiles.length; i += 1) { + let glob: string[]; + const orig = (<{orig?: {src?: string[] | string}}>resolvedFiles[i]).orig; + if (orig && ('src' in orig)) { + glob = [].concat(orig.src); + } + let compilationSet = { - src: _.map(files[i].src, (fileName) => utils.enclosePathInQuotesIfRequired(fileName)), - out: utils.enclosePathInQuotesIfRequired(files[i].out), - outDir: utils.enclosePathInQuotesIfRequired(files[i].outDir) + src: _.map(resolvedFiles[i].src, (fileName) => utils.enclosePathInQuotesIfRequired(fileName)), + out: utils.enclosePathInQuotesIfRequired(resolvedFiles[i].out), + outDir: utils.enclosePathInQuotesIfRequired(resolvedFiles[i].outDir), + glob }; - if ('dest' in files[i] && files[i].dest) { + if ('dest' in resolvedFiles[i] && resolvedFiles[i].dest) { let dest: string; - if (_.isArray(files[i].dest)) { + if (_.isArray(resolvedFiles[i].dest)) { // using an array for dest is not supported. Only take first element. - dest = files[i].dest[0]; + dest = resolvedFiles[i].dest[0]; } else { - dest = files[i].dest; + dest = resolvedFiles[i].dest; } if (utils.isJavaScriptFile(dest)) { compilationSet.out = dest; @@ -340,10 +396,6 @@ function addressAssociatedOptionsAndResolveConflicts(options: IGruntTSOptions) { options.sourceMap = false; } - if (options.inlineSources && options.sourceMap) { - options.errors.push('It is not permitted to use inlineSources and sourceMap together. Use one or the other.'); - } - if (options.inlineSources && !options.sourceMap) { options.inlineSources = true; options.inlineSourceMap = true; @@ -363,14 +415,16 @@ function addressAssociatedOptionsAndResolveConflicts(options: IGruntTSOptions) { options.comments = !options.removeComments; } - if ('html' in options && options.CompilationTasks.length === 0) { - options.errors.push(`ERROR: option \`html\` provided without specifying corresponding TypeScript source files to ` + - `compile. The transform will not occur unless grunt-ts also expects to compile these files.`); + if ('html' in options && + (options.CompilationTasks.length === 0 || + !_.some(options.CompilationTasks, item => ((item.src || []).length > 0 || (item.glob || []).length > 0)))) { + + options.errors.push(`ERROR: option "html" provided without corresponding TypeScript source files or glob to ` + + `compile. The transform will not occur unless grunt-ts also expects to compile some files.`); } options.CompilationTasks.forEach(compileTask => { if (compileTask.out && compileTask.outDir) { - console.log(JSON.stringify(compileTask)); options.warnings.push( 'The parameter `out` is incompatible with `outDir`; pass one or the other - not both. Ignoring `out` and using `outDir`.' ); @@ -419,5 +473,13 @@ function applyGruntTSDefaults(options: IGruntTSOptions) { options.removeComments = GruntTSDefaults.removeComments; } + if (!('failOnTypeErrors' in options)) { + options.failOnTypeErrors = GruntTSDefaults.failOnTypeErrors; + } + + if (!('emitGruntEvents' in options)) { + options.emitGruntEvents = GruntTSDefaults.emitGruntEvents; + } + return options; } diff --git a/tasks-internal/modules/reference.js b/tasks-internal/modules/reference.js index 66918985..1aec0590 100644 --- a/tasks-internal/modules/reference.js +++ b/tasks-internal/modules/reference.js @@ -1,4 +1,5 @@ /// +"use strict"; var _ = require('lodash'); var _str = require('underscore.string'); var fs = require('fs'); diff --git a/tasks-internal/modules/templateCache.js b/tasks-internal/modules/templateCache.js index 53c45074..e43f51d2 100644 --- a/tasks-internal/modules/templateCache.js +++ b/tasks-internal/modules/templateCache.js @@ -1,4 +1,5 @@ /// +"use strict"; var _ = require('lodash'); var fs = require('fs'); var path = require('path'); diff --git a/tasks-internal/modules/transformers.js b/tasks-internal/modules/transformers.js index 40b4c39b..f6c706af 100644 --- a/tasks-internal/modules/transformers.js +++ b/tasks-internal/modules/transformers.js @@ -1,5 +1,6 @@ /// /// +"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } @@ -104,7 +105,7 @@ var BaseTransformer = (function () { // and fails if it is not found. BaseTransformer.tsTransformerMatch = '^///\\s*ts:{0}(=?)(.*)'; return BaseTransformer; -})(); +}()); // This is a separate class from BaseTransformer to make it easier to add non import/export transforms in the future var BaseImportExportTransformer = (function (_super) { __extends(BaseImportExportTransformer, _super); @@ -147,14 +148,14 @@ var BaseImportExportTransformer = (function (_super) { return result; }; return BaseImportExportTransformer; -})(BaseTransformer); +}(BaseTransformer)); var ImportTransformer = (function (_super) { __extends(ImportTransformer, _super); function ImportTransformer() { _super.call(this, 'import', '[,]', _.template('import <%=filename%> = require(\'<%= pathToFile %>\');'), true, true); } return ImportTransformer; -})(BaseImportExportTransformer); +}(BaseImportExportTransformer)); var ExportTransformer = (function (_super) { __extends(ExportTransformer, _super); function ExportTransformer(eol) { @@ -167,7 +168,7 @@ var ExportTransformer = (function (_super) { this.eol = eol; } return ExportTransformer; -})(BaseImportExportTransformer); +}(BaseImportExportTransformer)); var ReferenceTransformer = (function (_super) { __extends(ReferenceTransformer, _super); function ReferenceTransformer() { @@ -176,7 +177,7 @@ var ReferenceTransformer = (function (_super) { _super.call(this, 'ref', '', _.template('/// '), false, false); } return ReferenceTransformer; -})(BaseImportExportTransformer); +}(BaseImportExportTransformer)); var UnknownTransformer = (function (_super) { __extends(UnknownTransformer, _super); function UnknownTransformer() { @@ -189,7 +190,7 @@ var UnknownTransformer = (function (_super) { return [this.syntaxError]; }; return UnknownTransformer; -})(BaseTransformer); +}(BaseTransformer)); // This code fixes the line encoding to be per os. // I think it is the best option available at the moment. // I am open for suggestions diff --git a/tasks-internal/modules/tsconfig.js b/tasks-internal/modules/tsconfig.js index 60fd143e..784b7e14 100644 --- a/tasks-internal/modules/tsconfig.js +++ b/tasks-internal/modules/tsconfig.js @@ -73,7 +73,6 @@ function resolveAsync(applyTo, taskOptions, targetOptions, theTemplateProcessor, else { return reject('Error reading "' + projectFile + '": ' + JSON.stringify(ex)); } - return reject(ex); } try { var projectSpec; @@ -111,13 +110,19 @@ function warnOnBadConfiguration(options, projectSpec) { } function getGlobs(taskOptions, targetOptions) { var globs = null; - if (taskOptions && taskOptions.src) { - globs = taskOptions.src; + if (taskOptions && isStringOrArray(taskOptions.src)) { + globs = _.map(getFlatCloneOf([taskOptions.src]), function (item) { return templateProcessor(item, {}); }); } - if (targetOptions && targetOptions.src) { - globs = targetOptions.src; + if (targetOptions && isStringOrArray(targetOptions.src)) { + globs = _.map(getFlatCloneOf([targetOptions.src]), function (item) { return templateProcessor(item, {}); }); } return globs; + function isStringOrArray(thing) { + return (_.isArray(thing) || _.isString(thing)); + } + function getFlatCloneOf(array) { + return _.flatten(array).slice(); + } } function resolve_output_locations(options, projectSpec) { if (options.CompilationTasks @@ -158,6 +163,10 @@ function getTSConfigSettings(raw) { tsconfig: tsconfigName }; } + if (!('tsconfig' in raw.tsconfig) && + !raw.tsconfig.passThrough) { + raw.tsconfig.tsconfig = 'tsconfig.json'; + } return raw.tsconfig; } catch (ex) { @@ -174,17 +183,58 @@ function getTSConfigSettings(raw) { } } function applyCompilerOptions(applyTo, projectSpec) { - var result = applyTo || {}; - var co = projectSpec.compilerOptions; - var tsconfig = applyTo.tsconfig; + var result = applyTo || {}, co = projectSpec.compilerOptions, tsconfig = applyTo.tsconfig; if (!tsconfig.ignoreSettings && co) { - var sameNameInTSConfigAndGruntTS = ['declaration', 'emitDecoratorMetadata', - 'experimentalAsyncFunctions', 'experimentalDecorators', 'isolatedModules', - 'inlineSourceMap', 'inlineSources', 'jsx', 'mapRoot', 'module', - 'moduleResolution', 'newLine', 'noEmit', - 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noLib', 'noResolve', - 'out', 'outDir', 'preserveConstEnums', 'removeComments', 'rootDir', - 'sourceMap', 'sourceRoot', 'suppressImplicitAnyIndexErrors', 'target']; + // Go here for the tsconfig.json documentation: + // https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/tsconfig.json.md + // There is a link to http://json.schemastore.org/tsconfig + var sameNameInTSConfigAndGruntTS = [ + 'allowJs', + 'allowSyntheticDefaultImports', + 'allowUnreachableCode', + 'allowUnusedLabels', + // we do not support charset as we assume input files are UTF-8. + 'declaration', + 'emitBOM', + 'emitDecoratorMetadata', + 'experimentalAsyncFunctions', + 'experimentalDecorators', + 'forceConsistentCasingInFileNames', + 'isolatedModules', + 'inlineSourceMap', + 'inlineSources', + 'jsx', + // we do not support listFiles. + 'locale', + 'mapRoot', + 'module', + 'moduleResolution', + 'newLine', + 'noEmit', + 'noEmitHelpers', + 'noEmitOnError', + 'noFallthroughCasesInSwitch', + 'noImplicitAny', + 'noImplicitReturns', + 'noImplicitUseStrict', + 'noLib', + 'noResolve', + 'out', + 'outDir', + // outFile is handled below. + 'preserveConstEnums', + 'pretty', + 'reactNamespace', + 'removeComments', + 'rootDir', + 'skipDefaultLibCheck', + 'sourceMap', + 'sourceRoot', + 'stripInternal', + 'suppressExcessPropertyIndexErrors', + 'suppressImplicitAnyIndexErrors', + 'target' + ]; sameNameInTSConfigAndGruntTS.forEach(function (propertyName) { if ((propertyName in co) && !(propertyName in result)) { result[propertyName] = co[propertyName]; @@ -209,8 +259,7 @@ function applyCompilerOptions(applyTo, projectSpec) { if (!gruntfileGlobs) { throw new Error('The tsconfig option overwriteFilesGlob is set to true, but no glob was passed-in.'); } - var relPath = relativePathFromGruntfileToTSConfig(); - var gruntGlobsRelativeToTSConfig = []; + var relPath = relativePathFromGruntfileToTSConfig(), gruntGlobsRelativeToTSConfig = []; for (var i = 0; i < gruntfileGlobs.length; i += 1) { gruntfileGlobs[i] = gruntfileGlobs[i].replace(/\\/g, '/'); gruntGlobsRelativeToTSConfig.push(path.relative(relPath, gruntfileGlobs[i]).replace(/\\/g, '/')); @@ -234,40 +283,25 @@ function applyCompilerOptions(applyTo, projectSpec) { addUniqueRelativeFilesToSrc(projectSpec.files, src, absolutePathToTSConfig); } else { - if (!globExpander.isStub) { - var virtualGlob = []; - if (projectSpec.exclude && _.isArray(projectSpec.exclude)) { - virtualGlob.push(path.resolve(absolutePathToTSConfig, './*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, './*.tsx')); - var tsconfigExcludedDirectories = []; - projectSpec.exclude.forEach(function (exc) { - tsconfigExcludedDirectories.push(path.normalize(path.join(absolutePathToTSConfig, exc))); - }); - fs.readdirSync(absolutePathToTSConfig).forEach(function (item) { - var filePath = path.normalize(path.join(absolutePathToTSConfig, item)); - if (fs.statSync(filePath).isDirectory()) { - if (tsconfigExcludedDirectories.indexOf(filePath) === -1) { - virtualGlob.push(path.resolve(absolutePathToTSConfig, item, './**/*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, item, './**/*.tsx')); - } - } - }); - } - else { - virtualGlob.push(path.resolve(absolutePathToTSConfig, './**/*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, './**/*.tsx')); - } - var files = globExpander(virtualGlob); - // make files relative to the tsconfig.json file - for (var i = 0; i < files.length; i += 1) { - files[i] = path.relative(absolutePathToTSConfig, files[i]).replace(/\\/g, '/'); - } - projectSpec.files = files; - if (projectSpec.filesGlob) { - saveTSConfigSync(tsconfig.tsconfig, projectSpec); - } - addUniqueRelativeFilesToSrc(files, src, absolutePathToTSConfig); + var validPattern_1 = /\.tsx?$/i; + var excludedPaths_1 = []; + if (_.isArray(projectSpec.exclude)) { + excludedPaths_1 = projectSpec.exclude.map(function (filepath) { + return utils.makeRelativePath(absolutePathToTSConfig, path.resolve(absolutePathToTSConfig, filepath)); + }); + } + var files = utils.getFiles(absolutePathToTSConfig, function (filepath) { + return excludedPaths_1.indexOf(utils.makeRelativePath(absolutePathToTSConfig, filepath)) > -1 + || (fs.statSync(filepath).isFile() + && !validPattern_1.test(filepath)); + }).map(function (filepath) { + return utils.makeRelativePath(absolutePathToTSConfig, filepath); + }); + projectSpec.files = files; + if (projectSpec.filesGlob) { + saveTSConfigSync(tsconfig.tsconfig, projectSpec); } + addUniqueRelativeFilesToSrc(files, src, absolutePathToTSConfig); } return result; } diff --git a/tasks-internal/modules/tsconfig.ts b/tasks-internal/modules/tsconfig.ts index 10106f15..df361ef5 100644 --- a/tasks-internal/modules/tsconfig.ts +++ b/tasks-internal/modules/tsconfig.ts @@ -84,7 +84,6 @@ export function resolveAsync(applyTo: IGruntTSOptions, } else { return reject('Error reading "' + projectFile + '": ' + JSON.stringify(ex)); } - return reject(ex); } try { @@ -126,15 +125,24 @@ function warnOnBadConfiguration(options: IGruntTSOptions, projectSpec: ITSConfig function getGlobs(taskOptions: ITargetOptions, targetOptions: ITargetOptions) { let globs = null; - if (taskOptions && (taskOptions).src) { - globs = (taskOptions).src; + + if (taskOptions && isStringOrArray((taskOptions).src)) { + globs = _.map(getFlatCloneOf([(taskOptions).src]), item => templateProcessor(item, {})); } - if (targetOptions && (targetOptions).src) { - globs = (targetOptions).src; + if (targetOptions && isStringOrArray((targetOptions).src)) { + globs = _.map(getFlatCloneOf([(targetOptions).src]), item => templateProcessor(item, {})); } + return globs; -} + function isStringOrArray(thing: any) { + return (_.isArray(thing) || _.isString(thing)); + } + + function getFlatCloneOf(array: Array) { + return [...(_.flatten(array))]; + } +} function resolve_output_locations(options: IGruntTSOptions, projectSpec: ITSConfigFile) { if (options.CompilationTasks @@ -186,6 +194,10 @@ function getTSConfigSettings(raw: ITargetOptions): ITSConfigSupport { tsconfig: tsconfigName }; } + if (!('tsconfig' in raw.tsconfig) && + !(raw.tsconfig).passThrough) { + (raw.tsconfig).tsconfig = 'tsconfig.json'; + } return raw.tsconfig; } catch (ex) { if (ex.code === 'ENOENT') { @@ -202,18 +214,64 @@ function getTSConfigSettings(raw: ITargetOptions): ITSConfigSupport { } function applyCompilerOptions(applyTo: IGruntTSOptions, projectSpec: ITSConfigFile) { - const result: IGruntTSOptions = applyTo || {}; - const co = projectSpec.compilerOptions; - const tsconfig: ITSConfigSupport = applyTo.tsconfig; + const result: IGruntTSOptions = applyTo || {}, + co = projectSpec.compilerOptions, + tsconfig: ITSConfigSupport = applyTo.tsconfig; if (!tsconfig.ignoreSettings && co) { - const sameNameInTSConfigAndGruntTS = ['declaration', 'emitDecoratorMetadata', - 'experimentalAsyncFunctions', 'experimentalDecorators', 'isolatedModules', - 'inlineSourceMap', 'inlineSources', 'jsx', 'mapRoot', 'module', - 'moduleResolution', 'newLine', 'noEmit', - 'noEmitHelpers', 'noEmitOnError', 'noImplicitAny', 'noLib', 'noResolve', - 'out', 'outDir', 'preserveConstEnums', 'removeComments', 'rootDir', - 'sourceMap', 'sourceRoot', 'suppressImplicitAnyIndexErrors', 'target']; + + // Go here for the tsconfig.json documentation: + // https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/tsconfig.json.md + // There is a link to http://json.schemastore.org/tsconfig + + const sameNameInTSConfigAndGruntTS = [ + 'allowJs', + 'allowSyntheticDefaultImports', + 'allowUnreachableCode', + 'allowUnusedLabels', + // we do not support charset as we assume input files are UTF-8. + 'declaration', + 'emitBOM', + 'emitDecoratorMetadata', + 'experimentalAsyncFunctions', + 'experimentalDecorators', + 'forceConsistentCasingInFileNames', + 'isolatedModules', + 'inlineSourceMap', + 'inlineSources', + 'jsx', + // we do not support listFiles. + 'locale', + 'mapRoot', + 'module', + 'moduleResolution', + 'newLine', + 'noEmit', + 'noEmitHelpers', + 'noEmitOnError', + 'noFallthroughCasesInSwitch', + 'noImplicitAny', + 'noImplicitReturns', + 'noImplicitUseStrict', + 'noLib', + 'noResolve', + 'out', + 'outDir', + // outFile is handled below. + 'preserveConstEnums', + 'pretty', + 'reactNamespace', + 'removeComments', + 'rootDir', + 'skipDefaultLibCheck', + 'sourceMap', + 'sourceRoot', + 'stripInternal', + 'suppressExcessPropertyIndexErrors', + 'suppressImplicitAnyIndexErrors', + 'target' + // we do not support the native TypeScript watch. + ]; sameNameInTSConfigAndGruntTS.forEach(propertyName => { if ((propertyName in co) && !(propertyName in result)) { @@ -247,8 +305,9 @@ function applyCompilerOptions(applyTo: IGruntTSOptions, projectSpec: ITSConfigFi throw new Error('The tsconfig option overwriteFilesGlob is set to true, but no glob was passed-in.'); } - const relPath = relativePathFromGruntfileToTSConfig(); - const gruntGlobsRelativeToTSConfig: string[] = []; + const relPath = relativePathFromGruntfileToTSConfig(), + gruntGlobsRelativeToTSConfig: string[] = []; + for (let i = 0; i < gruntfileGlobs.length; i += 1) { gruntfileGlobs[i] = gruntfileGlobs[i].replace(/\\/g, '/'); gruntGlobsRelativeToTSConfig.push(path.relative(relPath, gruntfileGlobs[i]).replace(/\\/g, '/')); @@ -274,45 +333,29 @@ function applyCompilerOptions(applyTo: IGruntTSOptions, projectSpec: ITSConfigFi if (projectSpec.files) { addUniqueRelativeFilesToSrc(projectSpec.files, src, absolutePathToTSConfig); } else { - if (!(globExpander).isStub) { - - const virtualGlob: string[] = []; - - if (projectSpec.exclude && _.isArray(projectSpec.exclude)) { - virtualGlob.push(path.resolve(absolutePathToTSConfig, './*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, './*.tsx')); - - const tsconfigExcludedDirectories: string[] = []; - projectSpec.exclude.forEach(exc => { - tsconfigExcludedDirectories.push(path.normalize(path.join(absolutePathToTSConfig, exc))); - }); - fs.readdirSync(absolutePathToTSConfig).forEach(item => { - const filePath = path.normalize(path.join(absolutePathToTSConfig, item)); - if (fs.statSync(filePath).isDirectory()) { - if (tsconfigExcludedDirectories.indexOf(filePath) === -1) { - virtualGlob.push(path.resolve(absolutePathToTSConfig, item, './**/*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, item, './**/*.tsx')); - } - } - }); - } else { - virtualGlob.push(path.resolve(absolutePathToTSConfig, './**/*.ts')); - virtualGlob.push(path.resolve(absolutePathToTSConfig, './**/*.tsx')); - } - - const files = globExpander(virtualGlob); - - // make files relative to the tsconfig.json file - for (let i = 0; i < files.length; i += 1) { - files[i] = path.relative(absolutePathToTSConfig, files[i]).replace(/\\/g, '/'); - } + const validPattern = /\.tsx?$/i; + let excludedPaths: string[] = []; + if (_.isArray(projectSpec.exclude)) { + excludedPaths = projectSpec.exclude.map(filepath => + utils.makeRelativePath(absolutePathToTSConfig, path.resolve(absolutePathToTSConfig, filepath)) + ); + } - projectSpec.files = files; - if (projectSpec.filesGlob) { + const files = + utils.getFiles( absolutePathToTSConfig, filepath => + excludedPaths.indexOf(utils.makeRelativePath(absolutePathToTSConfig, filepath)) > -1 + || ( + fs.statSync(filepath).isFile() + && !validPattern.test(filepath) + ) + ).map(filepath => + utils.makeRelativePath(absolutePathToTSConfig, filepath) + ); + projectSpec.files = files; + if (projectSpec.filesGlob) { saveTSConfigSync(tsconfig.tsconfig, projectSpec); - } - addUniqueRelativeFilesToSrc(files, src, absolutePathToTSConfig); } + addUniqueRelativeFilesToSrc(files, src, absolutePathToTSConfig); } return result; diff --git a/tasks-internal/modules/utils.js b/tasks-internal/modules/utils.js index cb38489b..481a4e79 100644 --- a/tasks-internal/modules/utils.js +++ b/tasks-internal/modules/utils.js @@ -1,16 +1,19 @@ /// +"use strict"; var path = require('path'); var fs = require('fs'); +var os = require('os'); var util = require('util'); var _ = require('lodash'); var es6_promise_1 = require('es6-promise'); exports.grunt = require('grunt'); exports.eol = exports.grunt.util.linefeed; -function newLineIsRedundant(newLineParameter) { - return ((newLineParameter === 'CRLF' && exports.grunt.util.linefeed === '\r\n') || - (newLineParameter === 'LF' && exports.grunt.util.linefeed === '\n')); +function newLineIsRedundantForTsc(newLineParameter, operatingSystem) { + if (operatingSystem === void 0) { operatingSystem = os; } + return ((newLineParameter === 'CRLF' && operatingSystem.EOL === '\r\n') || + (newLineParameter === 'LF' && operatingSystem.EOL === '\n')); } -exports.newLineIsRedundant = newLineIsRedundant; +exports.newLineIsRedundantForTsc = newLineIsRedundantForTsc; function newLineActualAsParameter(actualNewLineChars) { if (actualNewLineChars) { return actualNewLineChars.replace(/\n/g, 'LF').replace(/\r/g, 'CR'); @@ -341,12 +344,18 @@ function asyncSeries(items, callPerItem) { }); } exports.asyncSeries = asyncSeries; -function copyFileSync(srcFile, destFile, encoding) { +function copyFile(srcFile, destFile, callback, encoding) { if (encoding === void 0) { encoding = 'utf8'; } - var content = fs.readFileSync(srcFile, encoding); - fs.writeFileSync(destFile, content, encoding); + fs.readFile(srcFile, encoding, function (err, data) { + fs.writeFile(destFile, data, encoding, function (err) { + if (err) { + return callback(err); + } + return callback(); + }); + }); } -exports.copyFileSync = copyFileSync; +exports.copyFile = copyFile; function readAndParseJSONFromFileSync(fileName, encoding) { if (encoding === void 0) { encoding = 'utf8'; } var textContent, result; diff --git a/tasks-internal/modules/utils.ts b/tasks-internal/modules/utils.ts index ddaae027..917fc1d0 100644 --- a/tasks-internal/modules/utils.ts +++ b/tasks-internal/modules/utils.ts @@ -2,6 +2,7 @@ import path = require('path'); import fs = require('fs'); +import os = require('os'); import util = require('util'); import _ = require('lodash'); import {Promise} from 'es6-promise'; @@ -9,9 +10,9 @@ import {Promise} from 'es6-promise'; export var grunt: IGrunt = require('grunt'); export const eol: string = grunt.util.linefeed; -export function newLineIsRedundant(newLineParameter: string) { - return ((newLineParameter === 'CRLF' && grunt.util.linefeed === '\r\n') || - (newLineParameter === 'LF' && grunt.util.linefeed === '\n')); +export function newLineIsRedundantForTsc(newLineParameter: string, operatingSystem: {EOL: string} = os) { + return ((newLineParameter === 'CRLF' && operatingSystem.EOL === '\r\n') || + (newLineParameter === 'LF' && operatingSystem.EOL === '\n')); } export function newLineActualAsParameter(actualNewLineChars: string) { @@ -365,9 +366,15 @@ export function asyncSeries(items: U[], callPerItem: (item: U) => Promise< }); } -export function copyFileSync(srcFile: string, destFile: string, encoding = 'utf8') { - var content = fs.readFileSync(srcFile, encoding); - fs.writeFileSync(destFile, content, encoding); +export function copyFile(srcFile: string, destFile: string, callback: (err?: Error) => any, encoding = 'utf8') { + fs.readFile(srcFile, encoding, (err, data) => { + fs.writeFile(destFile, data, encoding, (err) => { + if (err) { + return callback(err); + } + return callback(); + }); + }); } export function readAndParseJSONFromFileSync(fileName: string, encoding = 'utf8') : any { diff --git a/tasks-internal/modules/visualStudioOptionsResolver.js b/tasks-internal/modules/visualStudioOptionsResolver.js index 5c498021..a7b75f46 100644 --- a/tasks-internal/modules/visualStudioOptionsResolver.js +++ b/tasks-internal/modules/visualStudioOptionsResolver.js @@ -94,15 +94,15 @@ function applyVSOptions(options, vsSettings) { if (options.CompilationTasks.length === 0) { options.CompilationTasks.push({ src: [] }); } - var src = options.CompilationTasks[0].src; - var absolutePathToVSProjectFolder = path.resolve(vsSettings.VSProjectDetails.ProjectFileName, '..'); - var gruntfileFolder = path.resolve('.'); + var src_1 = options.CompilationTasks[0].src; + var absolutePathToVSProjectFolder_1 = path.resolve(vsSettings.VSProjectDetails.ProjectFileName, '..'); + var gruntfileFolder_1 = path.resolve('.'); _.map(_.uniq(vsSettings.files), function (file) { - var absolutePathToFile = path.normalize(path.join(absolutePathToVSProjectFolder, file)); - var relativePathToFileFromGruntfile = path.relative(gruntfileFolder, absolutePathToFile).replace(new RegExp('\\' + path.sep, 'g'), '/'); - if (src.indexOf(absolutePathToFile) === -1 && - src.indexOf(relativePathToFileFromGruntfile) === -1) { - src.push(relativePathToFileFromGruntfile); + var absolutePathToFile = path.normalize(path.join(absolutePathToVSProjectFolder_1, file)); + var relativePathToFileFromGruntfile = path.relative(gruntfileFolder_1, absolutePathToFile).replace(new RegExp('\\' + path.sep, 'g'), '/'); + if (src_1.indexOf(absolutePathToFile) === -1 && + src_1.indexOf(relativePathToFileFromGruntfile) === -1) { + src_1.push(relativePathToFileFromGruntfile); } }); } @@ -115,12 +115,17 @@ function relativePathToVSProjectFolderFromGruntfile(settings) { return path.resolve(settings.VSProjectDetails.ProjectFileName, '..'); } function applyVSSettings(options, vsSettings) { - // TODO: support TypeScript 1.5 VS options. + // Visit this page for MSBuild documentation: + // https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Compiler%20Options%20in%20MSBuild.md var simpleVSSettingsToGruntTSMappings = { + 'AllowSyntheticDefaultImports': 'allowSyntheticDefaultImports', + 'AllowUnusedLabels': 'allowUnusedLabels', + 'AllowUnreachableCode': 'allowUnreachableCode', 'EmitBOM': 'emitBom', 'EmitDecoratorMetadata': 'emitDecoratorMetadata', 'ExperimentalAsyncFunctions': 'experimentalAsyncFunctions', 'ExperimentalDecorators': 'experimentalDecorators', + 'ForceConsistentCasingInFileNames': 'forceConsistentCasingInFileNames', 'GeneratesDeclarations': 'declaration', 'InlineSourceMap': 'inlineSourceMap', 'InlineSources': 'inlineSources', @@ -132,13 +137,18 @@ function applyVSSettings(options, vsSettings) { 'NewLine': 'newLine', 'NoEmitOnError': 'noEmitOnError', 'NoEmitHelpers': 'NoEmitHelpers', + 'NoFallthroughCasesInSwitch': 'noFallthroughCasesInSwitch', 'NoImplicitAny': 'noImplicitAny', + 'NoImplicitReturns': 'noImplicitReturns', + 'noImplicitUseStrict': 'NoImplicitUseStrict', 'NoLib': 'noLib', 'NoResolve': 'noResolve', - // OutFile and OutDir are resolved elsewhere + // OutFile (both out and outFile) and OutDir are resolved elsewhere 'PreserveConstEnums': 'preserveConstEnums', + 'ReactNamespace': 'reactNamespace', 'RemoveComments': 'removeComments', 'RootDir': 'rootDir', + 'SkipDefaultLibCheck': 'skipDefaultLibCheck', 'SourceMap': 'sourceMap', 'SourceRoot': 'sourceRoot', 'SuppressImplicitAnyIndexErrors': 'suppressImplicitAnyIndexErrors', diff --git a/tasks-internal/modules/visualStudioOptionsResolver.ts b/tasks-internal/modules/visualStudioOptionsResolver.ts index 80848e5f..b128eb46 100644 --- a/tasks-internal/modules/visualStudioOptionsResolver.ts +++ b/tasks-internal/modules/visualStudioOptionsResolver.ts @@ -139,12 +139,18 @@ function relativePathToVSProjectFolderFromGruntfile(settings: csproj2ts.TypeScri function applyVSSettings(options: IGruntTSOptions, vsSettings: csproj2ts.TypeScriptSettings) { - // TODO: support TypeScript 1.5 VS options. + // Visit this page for MSBuild documentation: + // https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Compiler%20Options%20in%20MSBuild.md + const simpleVSSettingsToGruntTSMappings = { + 'AllowSyntheticDefaultImports': 'allowSyntheticDefaultImports', + 'AllowUnusedLabels': 'allowUnusedLabels', + 'AllowUnreachableCode': 'allowUnreachableCode', 'EmitBOM': 'emitBom', 'EmitDecoratorMetadata': 'emitDecoratorMetadata', 'ExperimentalAsyncFunctions': 'experimentalAsyncFunctions', 'ExperimentalDecorators': 'experimentalDecorators', + 'ForceConsistentCasingInFileNames': 'forceConsistentCasingInFileNames', 'GeneratesDeclarations': 'declaration', 'InlineSourceMap': 'inlineSourceMap', 'InlineSources': 'inlineSources', @@ -156,13 +162,18 @@ function applyVSSettings(options: IGruntTSOptions, vsSettings: csproj2ts.TypeScr 'NewLine': 'newLine', 'NoEmitOnError': 'noEmitOnError', 'NoEmitHelpers': 'NoEmitHelpers', + 'NoFallthroughCasesInSwitch': 'noFallthroughCasesInSwitch', 'NoImplicitAny': 'noImplicitAny', + 'NoImplicitReturns': 'noImplicitReturns', + 'noImplicitUseStrict': 'NoImplicitUseStrict', 'NoLib': 'noLib', 'NoResolve': 'noResolve', - // OutFile and OutDir are resolved elsewhere + // OutFile (both out and outFile) and OutDir are resolved elsewhere 'PreserveConstEnums': 'preserveConstEnums', + 'ReactNamespace': 'reactNamespace', 'RemoveComments': 'removeComments', 'RootDir': 'rootDir', + 'SkipDefaultLibCheck': 'skipDefaultLibCheck', 'SourceMap': 'sourceMap', 'SourceRoot': 'sourceRoot', 'SuppressImplicitAnyIndexErrors': 'suppressImplicitAnyIndexErrors', diff --git a/tasks-internal/ts-internal.js b/tasks-internal/ts-internal.js index 835ce18a..5b8575df 100644 --- a/tasks-internal/ts-internal.js +++ b/tasks-internal/ts-internal.js @@ -1,4 +1,4 @@ -// v5.1.0 2015-10-14T19:47:01.693Z +// v5.5.0 2016-05-15T19:39:56.955Z /// /// 'use strict'; @@ -19,6 +19,7 @@ var templateCacheModule = require('./modules/templateCache'); var transformers = require('./modules/transformers'); var optionsResolver = require('../tasks/modules/optionsResolver'); var asyncSeries = utils.asyncSeries, timeIt = utils.timeIt; +var fail_event = 'grunt-ts.failure'; function pluginFn(grunt) { ///////////////////////////////////////////////////////////////////// // The grunt task @@ -28,14 +29,14 @@ function pluginFn(grunt) { var filesCompilationIndex = 0; var done, options; { - var currentTask = this; - var files = currentTask.files; + var currentGruntTask = this; + var resolvedFiles = currentGruntTask.files; // make async - done = currentTask.async(); + done = currentGruntTask.async(); // get unprocessed templates from configuration - var rawTaskConfig = (grunt.config.getRaw(currentTask.name) || {}); - var rawTargetConfig = (grunt.config.getRaw(currentTask.name + '.' + currentTask.target) || {}); - optionsResolver.resolveAsync(rawTaskConfig, rawTargetConfig, currentTask.target, files, grunt.template.process, grunt.file.expand).then(function (result) { + var rawTaskConfig = (grunt.config.getRaw(currentGruntTask.name) || {}); + var rawTargetConfig = (grunt.config.getRaw(currentGruntTask.name + '.' + currentGruntTask.target) || {}); + optionsResolver.resolveAsync(rawTaskConfig, rawTargetConfig, currentGruntTask.target, resolvedFiles, grunt.template.process, grunt.file.expand).then(function (result) { options = result; options.warnings.forEach(function (warning) { grunt.log.writeln(warning.magenta); @@ -44,6 +45,9 @@ function pluginFn(grunt) { grunt.log.writeln(error.red); }); if (options.errors.length > 0) { + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } done(false); return; } @@ -210,12 +214,15 @@ function pluginFn(grunt) { return isSuccessfulBuild; }).catch(function (err) { grunt.log.writeln(('Error: ' + err).red); + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } return false; }); } // Find out which files to compile, codegen etc. // Then calls the appropriate functions + compile function on those files - function filterFilesAndCompile() { + function filterFilesTransformAndCompile() { var filesToCompile = []; if (currentFiles.src || options.vs) { _.map(currentFiles.src, function (file) { @@ -230,14 +237,6 @@ function pluginFn(grunt) { }); } else { - // todo: fix this. - // if (_.isArray(options.files)) { - // filesToCompile = grunt.file.expand(files[filesCompilationIndex].src); - // } else if (options.files[target.dest]) { - // filesToCompile = grunt.file.expand(files[target.dest]); - // } else { - // filesToCompile = grunt.file.expand([(<{ src: string }>options.files).src]); - // } filesCompilationIndex += 1; } // ignore directories, and clear the files of output.d.ts and baseDirFile @@ -250,7 +249,7 @@ function pluginFn(grunt) { // compile html files must be before reference file creation var generatedFiles = []; if (options.html) { - var html2tsOptions = { + var html2tsOptions_1 = { moduleFunction: _.template(options.htmlModuleTemplate), varFunction: _.template(options.htmlVarTemplate), htmlOutputTemplate: options.htmlOutputTemplate, @@ -259,7 +258,13 @@ function pluginFn(grunt) { eol: (options.newLine || utils.eol) }; var htmlFiles = grunt.file.expand(options.html); - generatedFiles = _.map(htmlFiles, function (filename) { return html2tsModule.compileHTML(filename, html2tsOptions); }); + generatedFiles = _.map(htmlFiles, function (filename) { return html2tsModule.compileHTML(filename, html2tsOptions_1); }); + generatedFiles.forEach(function (fileName) { + if (filesToCompile.indexOf(fileName) === -1 && + grunt.file.isMatch(currentFiles.glob, fileName)) { + filesToCompile.push(fileName); + } + }); } ///// Template cache // Note: The template cache files do not go into generated files. @@ -347,7 +352,7 @@ function pluginFn(grunt) { // Reset the time for last compile call lastCompile = new Date().getTime(); // Run initial compile - return filterFilesAndCompile(); + return filterFilesTransformAndCompile(); // local event to handle file event function handleFileEvent(filepath, displaystr, addedOrChanged) { if (addedOrChanged === void 0) { addedOrChanged = false; } @@ -364,7 +369,7 @@ function pluginFn(grunt) { } // Log and run the debounced version. grunt.log.writeln((displaystr + ' >>' + filepath).yellow); - filterFilesAndCompile(); + filterFilesTransformAndCompile(); } }).then(function (res) { // Ignore res? (either logs or throws) @@ -372,6 +377,9 @@ function pluginFn(grunt) { if (res.some(function (success) { return !success; })) { + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } done(false); } else { diff --git a/tasks-internal/ts.ts b/tasks-internal/ts.ts index 2ac9dd58..21af93e6 100644 --- a/tasks-internal/ts.ts +++ b/tasks-internal/ts.ts @@ -21,6 +21,7 @@ import * as templateCacheModule from './modules/templateCache'; import * as transformers from './modules/transformers'; import * as optionsResolver from '../tasks/modules/optionsResolver'; const {asyncSeries, timeIt} = utils; +const fail_event = 'grunt-ts.failure'; function pluginFn(grunt: IGrunt) { @@ -36,18 +37,18 @@ function pluginFn(grunt: IGrunt) { options: IGruntTSOptions; { - const currentTask: grunt.task.IMultiTask = this; - const files: IGruntTSCompilationInfo[] = currentTask.files; + const currentGruntTask: grunt.task.IMultiTask = this; + const resolvedFiles: IGruntTSCompilationInfo[] = currentGruntTask.files; // make async - done = currentTask.async(); + done = currentGruntTask.async(); // get unprocessed templates from configuration let rawTaskConfig = - (grunt.config.getRaw(currentTask.name) || {}); + (grunt.config.getRaw(currentGruntTask.name) || {}); let rawTargetConfig = - (grunt.config.getRaw(currentTask.name + '.' + currentTask.target) || {}); + (grunt.config.getRaw(currentGruntTask.name + '.' + currentGruntTask.target) || {}); - optionsResolver.resolveAsync(rawTaskConfig, rawTargetConfig, currentTask.target, files, + optionsResolver.resolveAsync(rawTaskConfig, rawTargetConfig, currentGruntTask.target, resolvedFiles, grunt.template.process, grunt.file.expand).then((result) => { options = result; @@ -60,6 +61,9 @@ function pluginFn(grunt: IGrunt) { }); if (options.errors.length > 0) { + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } done(false); return; } @@ -221,7 +225,6 @@ function pluginFn(grunt: IGrunt) { } grunt.log.writeln(''); - if (isOnlyTypeErrors && !options.failOnTypeErrors) { grunt.log.write(('>> ').green); grunt.log.writeln('Type errors only.'); @@ -253,13 +256,16 @@ function pluginFn(grunt: IGrunt) { return isSuccessfulBuild; }).catch(function(err) { grunt.log.writeln(('Error: ' + err).red); + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } return false; }); } // Find out which files to compile, codegen etc. // Then calls the appropriate functions + compile function on those files - function filterFilesAndCompile(): Promise { + function filterFilesTransformAndCompile(): Promise { var filesToCompile: string[] = []; @@ -278,14 +284,6 @@ function pluginFn(grunt: IGrunt) { }); } else { - // todo: fix this. - // if (_.isArray(options.files)) { - // filesToCompile = grunt.file.expand(files[filesCompilationIndex].src); - // } else if (options.files[target.dest]) { - // filesToCompile = grunt.file.expand(files[target.dest]); - // } else { - // filesToCompile = grunt.file.expand([(<{ src: string }>options.files).src]); - // } filesCompilationIndex += 1; } @@ -312,6 +310,12 @@ function pluginFn(grunt: IGrunt) { let htmlFiles = grunt.file.expand(options.html); generatedFiles = _.map(htmlFiles, (filename) => html2tsModule.compileHTML(filename, html2tsOptions)); + generatedFiles.forEach((fileName) => { + if (filesToCompile.indexOf(fileName) === -1 && + grunt.file.isMatch(currentFiles.glob, fileName)) { + filesToCompile.push(fileName); + } + }); } ///// Template cache @@ -421,7 +425,7 @@ function pluginFn(grunt: IGrunt) { lastCompile = new Date().getTime(); // Run initial compile - return filterFilesAndCompile(); + return filterFilesTransformAndCompile(); // local event to handle file event function handleFileEvent(filepath: string, displaystr: string, addedOrChanged: boolean = false) { @@ -442,7 +446,7 @@ function pluginFn(grunt: IGrunt) { // Log and run the debounced version. grunt.log.writeln((displaystr + ' >>' + filepath).yellow); - filterFilesAndCompile(); + filterFilesTransformAndCompile(); } }).then((res: boolean[]) => { @@ -451,6 +455,9 @@ function pluginFn(grunt: IGrunt) { if (res.some((success: boolean) => { return !success; })) { + if (options.emitGruntEvents) { + grunt.event.emit(fail_event); + } done(false); } else { diff --git a/tasks/modules/utils.js b/tasks/modules/utils.js index 481a4e79..dfb3e201 100644 --- a/tasks/modules/utils.js +++ b/tasks/modules/utils.js @@ -268,6 +268,7 @@ function firstElementWithValue(elements, defaultResult) { result = item; return false; // break out of lodash loop } + return undefined; }); return result; } diff --git a/tasks/modules/utils.ts b/tasks/modules/utils.ts index 917fc1d0..ac25bf8d 100644 --- a/tasks/modules/utils.ts +++ b/tasks/modules/utils.ts @@ -281,6 +281,7 @@ export function firstElementWithValue(elements: T[], defaultResult: T = null) result = item; return false; // break out of lodash loop } + return undefined; }); return result; }