diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index e69de29..0000000 diff --git a/.travis.yml b/.travis.yml index d015b93..5e3bcf3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,5 @@ language: node_js node_js: - '10' - '8' -before_script: - - './node_modules/.bin/flow check' after_success: - './node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls' diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..1e7fd7c --- /dev/null +++ b/global.d.ts @@ -0,0 +1 @@ +declare module '*'; \ No newline at end of file diff --git a/lib/index.js b/lib/index.ts similarity index 74% rename from lib/index.js rename to lib/index.ts index 769a277..3c37bd2 100644 --- a/lib/index.js +++ b/lib/index.ts @@ -1,9 +1,7 @@ -// @flow -/* eslint-disable no-use-before-define */ import path from 'path'; import EventEmitter from 'events'; import {Readable} from 'stream'; -import url from 'url'; +import {parse as parseUrl} from 'url'; import arrayUniq from 'array-uniq'; import arrayDiffer from 'array-differ'; import easydate from 'easydate'; @@ -22,63 +20,65 @@ import template from 'lodash.template'; import plur from 'plur'; import unusedFilename from 'unused-filename'; -type PageresStream = Readable & {filename: string}; +interface PageresStream extends Readable { + filename: string; +} -type Options = { +interface Options { delay?: number; timeout?: number; crop?: boolean; incrementalName?: boolean; css?: string; - cookies?: Array | {[key: string]: string}; + cookies?: string[] | {[key: string]: string}; filename?: string; selector?: string; - hide?: Array; + hide?: string[]; username?: string; password?: string; scale?: number; format?: string; userAgent?: string; headers?: {[key: string]: string}; -}; +} -type Src = { +interface Src { url: string; - sizes: Array; - options: Options; -}; + sizes: string[]; + options?: Options; +} -type Viewport = { - url: string; - sizes: Array; - keywords: Array; -}; +type DestValue = string; -type SrcFn = - & ((_: void, _: void, _: void) => Array) - & ((url: string, sizes: Array, options: Options) => Pageres); +interface Viewport { + url: string; + sizes: string[]; + keywords: string[]; +} -type DestFn = - & ((_: void) => DestValue) - & ((dir: DestValue) => Pageres); +interface Stats { + urls?: number; + sizes?: number; + screenshots?: number; +} const getResMem = mem(getRes); const viewportListMem = mem(viewportList); -let listener; +let listener : NodeJS.Process; -export default class Pageres extends EventEmitter { +export default class Pageres extends EventEmitter { options: Options; - stats: Object; + stats: Stats; - items: Array; + items: PageresStream[]; - sizes: Array; + sizes: string[]; - urls: Array; + urls: string[]; - _src: Array; + _src: Src[]; _dest: DestValue; @@ -95,22 +95,28 @@ export default class Pageres extends EventEmitter { this.sizes = []; this.urls = []; this._src = []; + this._dest = ''; } - src: SrcFn; - - src(url: string, sizes: Array, options: Options) { + src() : Src[]; + src(url: string, sizes: string[], options?: Options) : this; + src(url?: string, sizes?: string[], options?: Options) : this | Src[] { if (url === undefined) { return this._src; } + if (sizes === undefined) { + throw new TypeError('Sizes required'); + } + this._src.push({url, sizes, options}); return this; } - dest: DestFn; + dest() : DestValue; + dest(dir: DestValue) : this; - dest(dir: DestValue) { + dest(dir?: DestValue) : this | DestValue { if (dir === undefined) { return this._dest; } @@ -120,15 +126,15 @@ export default class Pageres extends EventEmitter { } async run(): Promise { - await Promise.all(this.src().map(src => { // eslint-disable-line array-callback-return - const options = {...this.options, ...src.options}; - const sizes = arrayUniq(src.sizes.filter(/./.test, /^\d{2,4}x\d{2,4}$/i)); - const keywords = arrayDiffer(src.sizes, sizes); - + await Promise.all(this.src().map((src: Src) : Promise | void => { if (!src.url) { throw new Error('URL required'); } + const options = {...this.options, ...src.options}; + const sizes = arrayUniq(src.sizes.filter(/./.test, /^\d{2,4}x\d{2,4}$/i)); + const keywords = arrayDiffer(src.sizes, sizes); + this.urls.push(src.url); if (sizes.length === 0 && keywords.indexOf('w3counter') !== -1) { @@ -176,8 +182,8 @@ export default class Pageres extends EventEmitter { } } - save(streams: Array) { - const files = []; + async save(streams: PageresStream[]) { + const files: any[] = []; const end = () => del(files, {force: true}); @@ -188,8 +194,7 @@ export default class Pageres extends EventEmitter { }); } - return Promise.all(streams.map(stream => - // eslint-disable-next-line no-async-promise-executor + return Promise.all(streams.map(async stream => new Promise(async (resolve, reject) => { await makeDir(this.dest()); @@ -206,7 +211,7 @@ export default class Pageres extends EventEmitter { }); write.on('finish', resolve); - write.on('error', async err => { + write.on('error', async (err: any) => { await end(); reject(err); }); @@ -218,16 +223,10 @@ export default class Pageres extends EventEmitter { create(uri: string, size: string, options: Options) { const sizes = size.split('x'); const stream = screenshotStream(protocolify(uri), size, options); + const filename = template(`${options.filename}.${options.format}`); + const basename = path.isAbsolute(uri) ? path.basename(uri) : uri; - // Coercing to string here to please Flow - // TODO: Should fix the Flow type so this isn't necessary - const filename = template(`${String(options.filename)}.${String(options.format)}`); - - let hash = url.parse(uri).hash || ''; - - if (path.isAbsolute(uri)) { - uri = path.basename(uri); - } + let hash = parseUrl(uri).hash || ''; // Strip empty hash fragments: `#` `#/` `#!/` if (/^#!?\/?$/.test(hash)) { @@ -241,7 +240,7 @@ export default class Pageres extends EventEmitter { size, width: sizes[0], height: sizes[1], - url: filenamifyUrl(uri) + filenamify(hash) + url: `${filenamifyUrl(basename)}${filenamify(hash)}` }); if (options.incrementalName) { @@ -252,8 +251,7 @@ export default class Pageres extends EventEmitter { } successMessage() { - const {stats} = this; - const {screenshots, sizes, urls} = stats; + const {screenshots, sizes, urls} = this.stats; const words = { screenshots: plur('screenshot', screenshots), sizes: plur('size', sizes), diff --git a/package.json b/package.json index 264d226..c5a2875 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,10 @@ "node": ">=8" }, "scripts": { - "test": "xo && npm run prepublish && nyc ava", - "flow": "flow || true", - "prepublish": "babel lib --out-dir=dist" + "test": "npm run lint && npm run build && nyc ava", + "lint": "tslint --format stylish --project . && xo", + "build": "del dist && tsc", + "prepublish": "npm run build" }, "files": [ "dist" @@ -84,18 +85,11 @@ "viewport-list": "^5.0.1" }, "devDependencies": { + "@types/node": "^10.12.0", "ava": "*", - "babel-cli": "^6.7.5", - "babel-eslint": "^8.0.1", - "babel-plugin-add-module-exports": "^0.2.1", - "babel-plugin-transform-async-to-generator": "^6.7.4", - "babel-plugin-transform-flow-strip-types": "^6.14.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-plugin-transform-runtime": "^6.7.5", - "babel-preset-es2015-node4": "^2.1.0", "cookie": "^0.3.1", "coveralls": "^3.0.0", - "flow-bin": "^0.57.3", + "del-cli": "^1.1.0", "get-port": "^3.0.0", "get-stream": "^3.0.0", "image-size": "^0.6.0", @@ -104,25 +98,9 @@ "pify": "^3.0.0", "png-js": "^0.1.1", "sinon": "^4.0.1", + "tslint": "^5.11.0", + "tslint-xo": "^0.9.0", + "typescript": "^3.1.3", "xo": "*" - }, - "babel": { - "plugins": [ - "transform-async-to-generator", - "transform-object-rest-spread", - "transform-runtime", - "transform-flow-strip-types", - "add-module-exports" - ], - "presets": [ - "es2015-node4" - ], - "sourceMaps": "inline" - }, - "xo": { - "parser": "babel-eslint", - "rules": { - "generator-star-spacing": "off" - } } } diff --git a/test/test.js b/test/test.js index bf526bf..de74a48 100644 --- a/test/test.js +++ b/test/test.js @@ -202,11 +202,11 @@ test('when a file exists, append an incrementer', async t => { const folderPath = process.cwd(); try { await new Pageres({delay: 2}).src('yeoman.io', ['1024x768', '480x320'], {incrementalName: true, filename: '<%= url %>'}).dest(folderPath).run(); - t.true(fs.existsSync(path.join(folderPath, `yeoman.io.png`))); + t.true(fs.existsSync(path.join(folderPath, 'yeoman.io.png'))); await new Pageres({delay: 2}).src('yeoman.io', ['1024x768', '480x320'], {incrementalName: true, filename: '<%= url %>'}).dest(folderPath).run(); - t.true(fs.existsSync(path.join(folderPath, `yeoman.io (1).png`))); + t.true(fs.existsSync(path.join(folderPath, 'yeoman.io (1).png'))); } finally { - await fsP.unlink(path.join(folderPath, `yeoman.io.png`)); - await fsP.unlink(path.join(folderPath, `yeoman.io (1).png`)); + await fsP.unlink(path.join(folderPath, 'yeoman.io.png')); + await fsP.unlink(path.join(folderPath, 'yeoman.io (1).png')); } }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d62bd39 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2016", + "lib": [ + "es2016" + ], + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "declaration": true, + "pretty": true, + "newLine": "lf", + "stripInternal": true, + "strict": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noEmitOnError": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + }, + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..8731cf9 --- /dev/null +++ b/tslint.json @@ -0,0 +1,9 @@ +{ + "extends": "tslint-xo", + "rules": { + "variable-name": [ + true, + "allow-leading-underscore" + ] + } +}