Skip to content

Commit

Permalink
Merge branch 'release/v0.0.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
Magiczne committed Sep 14, 2020
2 parents 56265bf + 6ea2b79 commit 6df9ed3
Show file tree
Hide file tree
Showing 17 changed files with 3,662 additions and 109 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.idea/
node_modules/
screenshots/
tests/coverage

.lens.config.js
.lens.config.json
yarn-error.log
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
.eslintrc.json
.github/
.idea/
.lens.config.js
.lens.config.json
.yarnrc
.vscode/
jest.config.js
package-lock.json
tsconfig.json
screenshots
tests
yarn.lock
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ yarn global add @magiczne/lens
```
lens --help
Usage: -u <url>
Usage: lens -u <url>
Options:
--help Show help [boolean]
Expand All @@ -38,14 +38,54 @@ lens --help
space (e.g. 800x600 1920x1080) [string]
-t, --tag Custom tag that will be used as a subdirectory for
screenshots [string] [default: ""]
-o, --output-dir Output directory for the screenshots [string]
Examples:
lens -u https://example.com
lens -u https://example.com -r 1280x720
lens -u "https://example.com https://example.com/subpage" -r 1920x1080
lens -u https://example.com -r "800x600 1280x720"
lens -u https://example.com -r "800x600 1280x720" -o ./output
lens -u https://example.com -r 1280x720 -t "custom tag"
```

```lens``` will create a **screenshots** directory in a place where you will run it.
For example if you run it in ```C:\lens\``` it will create ```C:\lens\screenshots``` directory.
## Advanced configuration

```lens``` can be configured both locally and globally. When using ```lens``` globally, you can
configure it in either a ```.lens.config.js``` or ```.lens.config.json``` located in your home directory.
When using ```lens``` locally you can configure it using the same files, but located in your project directory.

Currently, these are options you can configure:

| Option | Type | Default | Description |
|---------------------|--------------------|--------------------------|------------------------------------------|
| directories.output | string | './screenshots' | Output directory for the screenshots. |
| puppeteer.headless | boolean | true | Whether to run browser in headless mode. |
| puppeteer.waitUntil | string \| string[] | ['load', 'networkidle2'] | When to consider navigation succedeed. Refer to the [puppeteer docs](https://github.com/puppeteer/puppeteer/blob/v5.3.0/docs/api.md#pagegotourl-options) for detailed information. |

Example config:

**.lens.config.js**
```javascript
module.exports = {
directories: {
output: './output'
},
puppeteer: {
headless: true,
waitUntil: ['load', 'domcontentloaded', 'networkidle2']
}
}
```

**.lens.config.json**
```json
{
"directories": {
"output": "./output"
},
"puppeteer": {
"headless": true,
"waitUntil": ["load", "domcontentloaded", "networkidle2"]
}
}
```
24 changes: 24 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
collectCoverageFrom: [
"**/*.{js,ts}",
"!**/node_modules/**",
"!**/vendor/**",
"!**/tests/**"
],
coverageDirectory: '<rootDir>/tests/coverage',
moduleFileExtensions: [
'ts', 'js'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
roots: [
'<rootDir>/src',
'<rootDir>/tests'
],
testEnvironment: 'node',
testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$',
transform: {
'^.+\\.ts?$': 'ts-jest'
}
}
2 changes: 1 addition & 1 deletion lib/index.js

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magiczne/lens",
"version": "0.0.2",
"version": "0.0.3",
"description": "Create screenshots of your webpage in different resolutions in a matter of seconds.",
"main": "./lib/index.js",
"bin": {
Expand All @@ -9,9 +9,11 @@
"scripts": {
"build:dev": "ncc build ./src/index.ts -e puppeteer -o lib",
"build:prod": "ncc build ./src/index.ts -e puppeteer -o lib -m",
"coverage": "jest --coverage",
"start": "ts-node -r tsconfig-paths/register src/index.ts",
"lint": "eslint . --ext .ts",
"lint-and-fix": "eslint . --ext .ts --fix"
"lint-and-fix": "eslint . --ext .ts --fix",
"test": "jest"
},
"engines": {
"node": ">=12"
Expand Down Expand Up @@ -41,6 +43,7 @@
},
"homepage": "https://github.com/Magiczne/lens",
"devDependencies": {
"@types/lodash": "^4.14.161",
"@types/node": "^14.10.0",
"@types/puppeteer": "^3.0.2",
"@types/yargs": "^15.0.5",
Expand All @@ -49,7 +52,14 @@
"@vercel/ncc": "^0.24.0",
"boxen": "^4.2.0",
"chalk": "^4.1.0",
"cosmiconfig": "^7.0.0",
"eslint": "^7.8.1",
"is-installed-globally": "^0.3.2",
"jest": "^26.4.2",
"jest-each": "^26.4.2",
"lodash": "^4.17.20",
"pkg-dir": "^4.2.0",
"ts-jest": "^26.3.0",
"ts-node": "^9.0.0",
"tsconfig-paths": "^3.9.0",
"typescript": "^4.0.2",
Expand Down
78 changes: 78 additions & 0 deletions src/argument-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import path from 'path'
import nodeUrl, { UrlWithStringQuery } from 'url'

import { ArgumentParser, LensArguments, ParsedLensArguments, Resolution } from '@/typings/types'
import { defaultResolutions } from '@/resolutions'
import { LensResolutionError } from '@/errors'

export default class DefaultArgumentParser implements ArgumentParser {
parse (args: LensArguments): ParsedLensArguments {
return {
urls: this.parseUrls(args.url),
resolutions: this.parseResolution(args.resolution),
tag: args.tag,

outputDir: this.parseDirectory(args.outputDir)
}
}

/**
* Resolve path to directory if present.
* Return undefined otherwise.
*
* @param dir
* @private
*/
private parseDirectory (dir?: string): string | undefined {
if (dir) {
return path.resolve(dir)
}

return undefined
}

/**
* Retrieve all resolutions from the input argument.
* If argument is not present, use default resolutions list.
*
* @param resolution
* @private
*/
private parseResolution (resolution?: string): Record<string, Resolution[]> {
if (!resolution) {
return defaultResolutions
}

const resolutions = resolution.split(' ')
.map(res => {
const parsedResolution = res.trim()
.split('x')
.map(x => parseInt(x, 10))

if (parsedResolution.length !== 2) {
throw new LensResolutionError(`Invalid resolution ${res}. It should follow format [width]x[height]`)
}

return parsedResolution
})
.map(res => {
return {
width: res[0],
height: res[1]
}
})

return { default: resolutions }
}

/**
* Retrieve all urls from the input arguments
*
* @param url
* @private
*/
private parseUrls (url: string): UrlWithStringQuery[] {
return url.split(' ')
.map(u => nodeUrl.parse(u))
}
}
51 changes: 51 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { cosmiconfig } from 'cosmiconfig'
import { CosmiconfigResult } from 'cosmiconfig/dist/types'
import isInstalledGlobally from 'is-installed-globally'
import { defaultsDeep } from 'lodash'
import os from 'os'
import packageDir from 'pkg-dir'
import path from 'path'

import { LensConfig } from '@/typings/types'

const defaultConfig: LensConfig = {
directories: {
output: path.resolve('./screenshots')
},
puppeteer: {
headless: true,
waitUntil: [
'load', 'networkidle2'
]
}
}

const config = async (): Promise<LensConfig> => {
const searchDirectory = isInstalledGlobally
? os.homedir()
: await packageDir()

const searchPlaces = [
'.lens.config.json',
'.lens.config.js'
]

const explorer = cosmiconfig('lens', {
searchPlaces: searchPlaces,
stopDir: searchDirectory,
transform (result: CosmiconfigResult): CosmiconfigResult {
// Resolve output filepath
if (result.config.directories && result.config.directories.output) {
result.config.directories.output = path.resolve(result.config.directories.output)
}

return result
}
})

const { config } = (await explorer.search(searchDirectory)) || { config: {} }

return defaultsDeep(config, defaultConfig)
}

export default config
20 changes: 20 additions & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class LensError extends Error {
constructor (message: string) {
super(message)

this.name = 'LensError'
}
}

class LensResolutionError extends LensError {
constructor (message: string) {
super(message)

this.name = 'LensResolutionError'
}
}

export {
LensError,
LensResolutionError
}
36 changes: 29 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import yargs from 'yargs'

import config from '@/config'
import { LensArguments } from '@/typings/types'
import Lens from '@/lens'
import DefaultArgumentParser from '@/argument-parser'
import ConsoleLogger from '@/logging/console-logger'

const args: LensArguments = yargs
.usage('Usage: -u <url>')
.usage('Usage: lens -u <url>')
.option('url', {
alias: 'u',
describe: 'The url from which screenshots will be taken. ' +
Expand All @@ -27,20 +30,39 @@ const args: LensArguments = yargs
string: true,
default: ''
})
.demandOption(['url'], 'Please provide both url and resolution arguments to work with this tool')
.option('output-dir', {
alias: 'o',
describe: 'Output directory for the screenshots',
string: true
})
.demandOption(['url'], 'You need to provide at least the "url" parameter to work with this tool')
.epilogue('For advanced usage documentation please visit https://github.com/Magiczne/lens')
.example('lens -u https://example.com', '')
.example('lens -u https://example.com -r 1280x720', '')
.example('lens -u "https://example.com https://example.com/subpage" -r 1920x1080', '')
.example('lens -u https://example.com -r "800x600 1280x720"', '')
.example('lens -u https://example.com -r "800x600 1280x720" -o ./output', '')
.example('lens -u https://example.com -r 1280x720 -t "custom tag"', '')
.argv

const main = async () => {
const lens = new Lens(args)
await lens.init()
await lens.run()
await lens.dispose()
const lensConfig = await config()
const logger = new ConsoleLogger()

try {
const lens = new Lens({
argumentParser: new DefaultArgumentParser(),
logger
})
await lens.init(args, lensConfig)
await lens.run()
await lens.dispose()
} catch (e) {
if (e.name.startsWith('Lens')) {
logger.error(e.message)
} else {
throw e
}
}
}

main()
Loading

0 comments on commit 6df9ed3

Please sign in to comment.