diff --git a/custom_typings/fs-extra.d.ts b/custom_typings/fs-extra.d.ts deleted file mode 100644 index cdc20774..00000000 --- a/custom_typings/fs-extra.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'fs-extra' { - export function copySync(source: string, dest: string): void; - export function readdirSync(path: string): string; -} diff --git a/custom_typings/node.d.ts b/custom_typings/node.d.ts deleted file mode 100644 index 3e3e84dd..00000000 --- a/custom_typings/node.d.ts +++ /dev/null @@ -1,151 +0,0 @@ -// copied from lib.es6.d.ts - -declare var console: Console; - -interface Console { - assert(test?: boolean, message?: string, ...optionalParams: any[]): void; - clear(): void; - count(countTitle?: string): void; - debug(message?: string, ...optionalParams: any[]): void; - dir(value?: any, ...optionalParams: any[]): void; - dirxml(value: any): void; - error(message?: any, ...optionalParams: any[]): void; - group(groupTitle?: string): void; - groupCollapsed(groupTitle?: string): void; - groupEnd(): void; - info(message?: any, ...optionalParams: any[]): void; - log(message?: any, ...optionalParams: any[]): void; - // msIsIndependentlyComposed(element: Element): boolean; - profile(reportName?: string): void; - profileEnd(): void; - // select(element: Element): void; - time(timerName?: string): void; - timeEnd(timerName?: string): void; - trace(message?: any, ...optionalParams: any[]): void; - warn(message?: any, ...optionalParams: any[]): void; -} - -declare var Console: { - prototype: Console; - new(): Console; -} - -declare module Intl { - interface CollatorOptions { - usage?: string; - localeMatcher?: string; - numeric?: boolean; - caseFirst?: string; - sensitivity?: string; - ignorePunctuation?: boolean; - } - - interface ResolvedCollatorOptions { - locale: string; - usage: string; - sensitivity: string; - ignorePunctuation: boolean; - collation: string; - caseFirst: string; - numeric: boolean; - } - - interface Collator { - compare(x: string, y: string): number; - resolvedOptions(): ResolvedCollatorOptions; - } - var Collator: { - new (locales?: string[], options?: CollatorOptions): Collator; - new (locale?: string, options?: CollatorOptions): Collator; - (locales?: string[], options?: CollatorOptions): Collator; - (locale?: string, options?: CollatorOptions): Collator; - supportedLocalesOf(locales: string[], options?: CollatorOptions): string[]; - supportedLocalesOf(locale: string, options?: CollatorOptions): string[]; - } - - interface NumberFormatOptions { - localeMatcher?: string; - style?: string; - currency?: string; - currencyDisplay?: string; - useGrouping?: boolean; - minimumIntegerDigits?: number; - minimumFractionDigits?: number; - maximumFractionDigits?: number; - minimumSignificantDigits?: number; - maximumSignificantDigits?: number; - } - - interface ResolvedNumberFormatOptions { - locale: string; - numberingSystem: string; - style: string; - currency?: string; - currencyDisplay?: string; - minimumIntegerDigits: number; - minimumFractionDigits: number; - maximumFractionDigits: number; - minimumSignificantDigits?: number; - maximumSignificantDigits?: number; - useGrouping: boolean; - } - - interface NumberFormat { - format(value: number): string; - resolvedOptions(): ResolvedNumberFormatOptions; - } - var NumberFormat: { - new (locales?: string[], options?: NumberFormatOptions): NumberFormat; - new (locale?: string, options?: NumberFormatOptions): NumberFormat; - (locales?: string[], options?: NumberFormatOptions): NumberFormat; - (locale?: string, options?: NumberFormatOptions): NumberFormat; - supportedLocalesOf(locales: string[], options?: NumberFormatOptions): string[]; - supportedLocalesOf(locale: string, options?: NumberFormatOptions): string[]; - } - - interface DateTimeFormatOptions { - localeMatcher?: string; - weekday?: string; - era?: string; - year?: string; - month?: string; - day?: string; - hour?: string; - minute?: string; - second?: string; - timeZoneName?: string; - formatMatcher?: string; - hour12?: boolean; - timeZone?: string; - } - - interface ResolvedDateTimeFormatOptions { - locale: string; - calendar: string; - numberingSystem: string; - timeZone: string; - hour12?: boolean; - weekday?: string; - era?: string; - year?: string; - month?: string; - day?: string; - hour?: string; - minute?: string; - second?: string; - timeZoneName?: string; - } - - interface DateTimeFormat { - format(date?: Date | number): string; - resolvedOptions(): ResolvedDateTimeFormatOptions; - } - var DateTimeFormat: { - new (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat; - new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat; - (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat; - (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat; - supportedLocalesOf(locales: string[], options?: DateTimeFormatOptions): string[]; - supportedLocalesOf(locale: string, options?: DateTimeFormatOptions): string[]; - } -} diff --git a/custom_typings/vinyl-fs.d.ts b/custom_typings/vinyl-fs.d.ts deleted file mode 100644 index 6d394967..00000000 --- a/custom_typings/vinyl-fs.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -declare module "vinyl-fs" { - interface ISrcOptions { - allowEmpty?: boolean; - } -} diff --git a/gulpfile.js b/gulpfile.js index 3881ab7d..993abd9e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -23,33 +23,22 @@ const tslint = require('gulp-tslint'); const typescript = require('gulp-typescript'); const typings = require('gulp-typings'); -const tsProject = typescript.createProject('tsconfig.json'); - -// WTF gulp-typescript -class Rebase extends stream.Transform { - constructor() { - super({objectMode: true}); - } - - _transform(file, enc, callback) { - let oldPath = file.path; - if (oldPath.startsWith('lib/src')) { - file.path = 'lib' + oldPath.substring('lib/src'.length); - } - callback(null, file); - } -} +const tsProject = typescript.createProject('tsconfig.json', { + typescript: require('typescript') +}); gulp.task('init', () => gulp.src("./typings.json").pipe(typings())); gulp.task('lint', ['tslint', 'eslint', 'depcheck']); -gulp.task('build', () => - tsProject.src() - .pipe(typescript(tsProject)) - .pipe(new Rebase()) - .pipe(gulp.dest('lib')) -); +gulp.task('build', () => { + const tsResult = tsProject.src().pipe(typescript(tsProject)); + + return mergeStream( + tsResult.dts.pipe(gulp.dest('lib')), + tsResult.js.pipe(gulp.dest('lib')) + ); +}); gulp.task('clean', (done) => { fs.remove(path.join(__dirname, 'lib'), done); @@ -71,8 +60,9 @@ gulp.task('tslint', () => gulp.src('src/**/*.ts') .pipe(tslint({ configuration: 'tslint.json', + formatter: 'verbose' })) - .pipe(tslint.report('verbose'))); + .pipe(tslint.report())); gulp.task('eslint', () => gulp.src('test/**/*.js') @@ -81,7 +71,12 @@ gulp.task('eslint', () => .pipe(eslint.failAfterError())); gulp.task('depcheck', () => - depcheck(__dirname, {}) + depcheck(__dirname, { + // "@types/*" dependencies are type declarations that are automatically + // loaded by TypeScript during build. depcheck can't detect this + // so we ignore them here. + ignoreMatches: ['@types/*'] + }) .then((result) => { let invalidFiles = Object.keys(result.invalidFiles) || []; let invalidJsFiles = invalidFiles.filter((f) => f.endsWith('.js')); diff --git a/package.json b/package.json index 5d7351c7..bbf30a52 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ }, "homepage": "https://github.com/Polymer/polymer-build#readme", "dependencies": { + "@types/fs-extra": "0.0.32", + "@types/vinyl-fs": "0.0.28", "dom5": "^1.3.1", "fs-extra": "^0.30.0", "gulp": "^3.9.1", @@ -42,12 +44,13 @@ "depcheck": "^0.6.3", "gulp-eslint": "^2.0.0", "gulp-mocha": "^2.2.0", - "gulp-tslint": "^5.0.0", + "gulp-tslint": "^6.1.1", "gulp-typescript": "^2.13.4", "gulp-typings": "^2.0.0", "run-sequence": "^1.2.0", "temp": "^0.8.3", - "tslint": "^3.10.2", + "tslint": "^3.15.1", + "typescript": "^2.0.2", "vinyl-fs-fake": "^1.1.0" } } diff --git a/src/bundle.ts b/src/bundle.ts index a3eca736..1534f37e 100644 --- a/src/bundle.ts +++ b/src/bundle.ts @@ -9,7 +9,6 @@ */ import * as dom5 from 'dom5'; -import * as gulpif from 'gulp-if'; import * as path from 'path'; import {posix as posixPath} from 'path'; import {Transform} from 'stream'; @@ -104,68 +103,65 @@ export class Bundler extends Transform { return this.allFragments.indexOf(file.path) !== -1; } - _buildBundles(): Promise> { - return this._getBundles().then((bundles) => { - let sharedDepsBundle = (this.shell) - ? urlFromPath(this.root, this.shell) - : this.sharedBundleUrl; - let sharedDeps = bundles.get(sharedDepsBundle) || []; - let promises: Promise[] = []; - - if (this.shell) { - let shellFile = this.analyzer.getFile(this.shell); - console.assert(shellFile != null); - let newShellContent = this._addSharedImportsToShell(bundles); - shellFile.contents = new Buffer(newShellContent); - } + async _buildBundles(): Promise> { + let bundles = await this._getBundles(); + let sharedDepsBundle = (this.shell) + ? urlFromPath(this.root, this.shell) + : this.sharedBundleUrl; + let sharedDeps = bundles.get(sharedDepsBundle) || []; + let promises: Promise[] = []; + + if (this.shell) { + let shellFile = this.analyzer.getFile(this.shell); + console.assert(shellFile != null); + let newShellContent = this._addSharedImportsToShell(bundles); + shellFile.contents = new Buffer(newShellContent); + } - for (let fragment of this.allFragments) { - let fragmentUrl = urlFromPath(this.root, fragment); - let addedImports = (fragment === this.shell && this.shell) - ? [] - : [posixPath.relative(posixPath.dirname(fragmentUrl), sharedDepsBundle)]; - let excludes = (fragment === this.shell && this.shell) - ? [] - : sharedDeps.concat(sharedDepsBundle); - - promises.push(new Promise((resolve, reject) => { - let vulcanize = new Vulcanize({ - abspath: null, - fsResolver: this.analyzer.resolver, - addedImports: addedImports, - stripExcludes: excludes, - inlineScripts: true, - inlineCss: true, - inputUrl: fragmentUrl, - }); - vulcanize.process(null, (err: any, doc: string) => { - if (err) { - reject(err); - } else { - resolve({ - url: fragment, - contents: doc, - }); - } - }); - })); - } - // vulcanize the shared bundle - if (!this.shell && sharedDeps && sharedDeps.length !== 0) { - logger.info(`generating shared bundle...`); - promises.push(this._generateSharedBundle(sharedDeps)); - } - return Promise.all(promises).then((bundles) => { - // TODO(justinfagnani): remove at TypeScript 2.0 - let _bundles = bundles; - // convert {url,contents}[] into a Map - let contentsMap = new Map(); - for (let bundle of _bundles) { - contentsMap.set(bundle.url, bundle.contents); - } - return contentsMap; - }); - }); + for (let fragment of this.allFragments) { + let fragmentUrl = urlFromPath(this.root, fragment); + let addedImports = (fragment === this.shell && this.shell) + ? [] + : [posixPath.relative(posixPath.dirname(fragmentUrl), sharedDepsBundle)]; + let excludes = (fragment === this.shell && this.shell) + ? [] + : sharedDeps.concat(sharedDepsBundle); + + promises.push(new Promise((resolve, reject) => { + let vulcanize = new Vulcanize({ + abspath: null, + fsResolver: this.analyzer.resolver, + addedImports: addedImports, + stripExcludes: excludes, + inlineScripts: true, + inlineCss: true, + inputUrl: fragmentUrl, + }); + vulcanize.process(null, (err: any, doc: string) => { + if (err) { + reject(err); + } else { + resolve({ + url: fragment, + contents: doc, + }); + } + }); + })); + } + + // vulcanize the shared bundle + if (!this.shell && sharedDeps && sharedDeps.length !== 0) { + logger.info(`generating shared bundle...`); + promises.push(this._generateSharedBundle(sharedDeps)); + } + + let vulcanizedBundles = await Promise.all(promises); + let contentsMap = new Map(); + for (let bundle of vulcanizedBundles) { + contentsMap.set(bundle.url, bundle.contents); + } + return contentsMap; } _addSharedImportsToShell(bundles: Map): string { diff --git a/src/polymer-project.ts b/src/polymer-project.ts index fb1080ec..088f64da 100644 --- a/src/polymer-project.ts +++ b/src/polymer-project.ts @@ -180,7 +180,6 @@ export class PolymerProject { */ sources(): NodeJS.ReadableStream { return vfs.src(this.allSourceGlobs, { - allowEmpty: true, cwdbase: true, nodir: true, }); @@ -195,7 +194,6 @@ export class PolymerProject { // stream and pipe our default dependencyStream through it to combine. if (this.includeDependencies.length > 0) { let includeStream = vfs.src(this.includeDependencies, { - allowEmpty: true, cwdbase: true, nodir: true, passthrough: true, diff --git a/src/service-worker.ts b/src/service-worker.ts index 3a61bb3a..a6aca06a 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -8,11 +8,14 @@ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ +/// + import {writeFile} from 'fs'; import * as path from 'path'; import * as logging from 'plylog'; import {PolymerProject} from './polymer-project'; import {DepsIndex} from './analyzer'; + import {SWConfig, generate as swPrecacheGenerate} from 'sw-precache'; let logger = logging.getLogger('polymer-build.service-worker'); @@ -58,7 +61,7 @@ function getBundledPrecachedAssets(project: PolymerProject) { * Returns a promise that resolves with a generated service worker (the file * contents), based off of the options provided. */ -export function generateServiceWorker(options: AddServiceWorkerOptions): Promise { +export async function generateServiceWorker(options: AddServiceWorkerOptions): Promise { console.assert(!!options, '`project` & `buildRoot` options are required'); console.assert(!!options.project, '`project` option is required'); console.assert(!!options.buildRoot, '`buildRoot` option is required'); @@ -68,38 +71,37 @@ export function generateServiceWorker(options: AddServiceWorkerOptions): Promise let buildRoot = options.buildRoot; let swConfig: SWConfig = Object.assign({}, options.swConfig); - return project.analyzer.analyzeDependencies.then((depsIndex: DepsIndex) => { - let staticFileGlobs = Array.from(swConfig.staticFileGlobs || []); - let precachedAssets = (options.bundled) - ? getBundledPrecachedAssets(project) - : getPrecachedAssets(depsIndex, project); + let depsIndex = await project.analyzer.analyzeDependencies; + let staticFileGlobs = Array.from(swConfig.staticFileGlobs || []); + let precachedAssets = (options.bundled) + ? getBundledPrecachedAssets(project) + : getPrecachedAssets(depsIndex, project); + + staticFileGlobs = staticFileGlobs.concat(precachedAssets); + staticFileGlobs = staticFileGlobs.map((filePath: string) => { + if (filePath.startsWith(project.root)) { + filePath = filePath.substring(project.root.length); + } + return path.join(buildRoot, filePath); + }); - staticFileGlobs = staticFileGlobs.concat(precachedAssets); - staticFileGlobs = staticFileGlobs.map((filePath: string) => { - if (filePath.startsWith(project.root)) { - filePath = filePath.substring(project.root.length); + // swPrecache will determine the right urls by stripping buildRoot + swConfig.stripPrefix = buildRoot; + // static files will be pre-cached + swConfig.staticFileGlobs = staticFileGlobs; + // Log service-worker helpful output at the debug log level + swConfig.logger = swConfig.logger || logger.debug; + + return await >(new Promise((resolve, reject) => { + logger.debug(`writing service worker...`, swConfig); + swPrecacheGenerate(swConfig, (err?: Error, fileContents?: string) => { + if (err) { + reject(err); + } else { + resolve(new Buffer(fileContents)); } - return path.join(buildRoot, filePath); }); - - // swPrecache will determine the right urls by stripping buildRoot - swConfig.stripPrefix = buildRoot; - // static files will be pre-cached - swConfig.staticFileGlobs = staticFileGlobs; - // Log service-worker helpful output at the debug log level - swConfig.logger = swConfig.logger || logger.debug; - - return new Promise((resolve, reject) => { - logger.debug(`writing service worker...`, swConfig); - swPrecacheGenerate(swConfig, (err?: Error, fileContents?: string) => { - if (err) { - reject(err); - } else { - resolve(new Buffer(fileContents)); - } - }); - }); - }); + })); } diff --git a/test/service-worker_test.js b/test/service-worker_test.js index 1804627c..e10df906 100644 --- a/test/service-worker_test.js +++ b/test/service-worker_test.js @@ -64,21 +64,36 @@ suite('service-worker', () => { suite('generateServiceWorker()', () => { test('should throw when options are not provided', () => { - assert.throws(() => { - serviceWorker.generateServiceWorker(); - }, Error.AssertionError, '`project` & `buildRoot` options are required'); + return serviceWorker.generateServiceWorker() + .then( + () => { assert.fail('generateServiceWorker() resolved, expected rejection!'); }, + (error) => { + assert.equal(error.name, 'AssertionError'); + assert.equal(error.message, '`project` & `buildRoot` options are required'); + } + ); }); test('should throw when options.project is not provided', () => { - assert.throws(() => { - serviceWorker.generateServiceWorker({buildRoot: testBuildRoot}); - }, Error.AssertionError, '`project` option is required'); + return serviceWorker.generateServiceWorker({buildRoot: testBuildRoot}) + .then( + () => { assert.fail('generateServiceWorker() resolved, expected rejection!'); }, + (error) => { + assert.equal(error.name, 'AssertionError'); + assert.equal(error.message, '`project` option is required'); + } + ); }); test('should throw when options.buildRoot is not provided', () => { - assert.throws(() => { - serviceWorker.generateServiceWorker({project: defaultProject}); - }, Error.AssertionError, '`buildRoot` option is required'); + return serviceWorker.generateServiceWorker({project: defaultProject}) + .then( + () => { assert.fail('generateServiceWorker() resolved, expected rejection!'); }, + (error) => { + assert.equal(error.name, 'AssertionError'); + assert.equal(error.message, '`buildRoot` option is required'); + } + ); }); test('should not modify the options object provided when called', () => { diff --git a/tsconfig.json b/tsconfig.json index 50f6706f..c45623d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,48 +1,20 @@ { "compilerOptions": { "target": "es6", + "lib": ["es2016"], "module": "commonjs", "moduleResolution": "node", "isolatedModules": false, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, "declaration": true, "noImplicitAny": true, "removeComments": false, - "noLib": true, "preserveConstEnums": true, "suppressImplicitAnyIndexErrors": false, "outDir": "lib", "rootDir": "src" }, - "filesGlob": [ - "node_modules/typescript/lib/lib.core.es7.d.ts", - "custom_typings/*.d.ts", - "src/**/*.ts", - "typings/index.d.ts" - ], - "files": [ - "node_modules/typescript/lib/lib.core.es7.d.ts", - "custom_typings/dom5.d.ts", - "custom_typings/fs-extra.d.ts", - "custom_typings/hydrolysis.d.ts", - "custom_typings/node.d.ts", - "custom_typings/plylog.d.ts", - "custom_typings/slash.d.ts", - "custom_typings/sw-precache.d.ts", - "custom_typings/vinyl-fs.d.ts", - "src/analyzer.ts", - "src/bundle.ts", - "src/fork-stream.ts", - "src/get-dependencies-from-document.ts", - "src/polymer-build.ts", - "src/polymer-project.ts", - "src/streams.ts", - "src/service-worker.ts", - "src/path-transformers.ts", - "typings/index.d.ts" - ], - "atom": { - "rewriteTsconfig": true - } + "include": [ + "custom_typings/**/*.ts", + "src/**/*.ts" + ] } diff --git a/typings.json b/typings.json index 476b9a6e..dc0f32dd 100644 --- a/typings.json +++ b/typings.json @@ -3,12 +3,10 @@ "glob": "registry:dt/glob#5.0.10+20160317120654", "glob-stream": "registry:dt/glob-stream#3.1.12+20160316155526", "gulp": "registry:dt/gulp#3.8.0+20160316155526", - "gulp-if": "registry:dt/gulp-if#0.0.0+20160316155526", "gulp-util": "registry:dt/gulp-util#0.0.0+20160316155526", "merge-stream": "registry:dt/merge-stream#1.0.0+20160313224417", "mime": "registry:dt/mime#0.0.0+20160316155526", "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", - "node": "registry:dt/node#4.0.0+20160412142033", "source-map": "registry:dt/source-map#0.0.0+20160317120654", "uglify-js": "registry:dt/uglify-js#2.6.1+20160316155526", "vinyl": "registry:dt/vinyl#1.1.0+20160316155526",