🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

esdoc-uploader

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

esdoc-uploader - npm Package Compare versions

Comparing version

to
2.0.0

.esdoc.json

67

package.json
{
"name": "esdoc-uploader",
"description": "Upload your ESDoc documentation to doc.esdoc.org",
"version": "1.0.1",
"homepage": "https://homer0.github.io/esdoc-uploader",
"version": "2.0.0",
"repository": "homer0/esdoc-uploader",

@@ -15,45 +16,35 @@ "author": "Leonardo Apiwan (@homer0) <me@homer0.com>",

],
"dependencies": {
"request": "2.65.0",
"log-util": "1.1.1"
},
"dependencies": {},
"devDependencies": {
"babel-cli": "6.1.2",
"babel-preset-es2015": "6.1.2",
"eslint": "1.8.0",
"babel-eslint": "4.1.4",
"jscs": "2.5.0",
"coveralls": "2.11.4",
"jest-cli": "0.7.1",
"babel-jest": "5.3.0",
"esdoc": "0.4.3",
"esdoc-es7-plugin": "0.0.3"
"@babel/preset-env": "7.7.1",
"@babel/core": "7.7.0",
"@babel/plugin-transform-runtime": "7.6.2",
"coveralls": "^3.0.7",
"esdoc": "^1.1.0",
"esdoc-standard-plugin": "^1.0.0",
"esdoc-node": "1.0.4",
"eslint": "^6.6.0",
"eslint-plugin-homer0": "^2.0.1",
"husky": "^3.0.9",
"jasmine-expect": "^4.0.3",
"jest-ex": "^6.1.1",
"jest-cli": "^24.9.0",
"wootils": "^2.6.5"
},
"scripts": {
"build": "babel --presets es2015 -d dist/ src/",
"prepublish": "npm run build",
"test": "jest ./__tests__/index-test.js",
"coverage": "npm test; open ./coverage/lcov-report/index.html",
"lint": "eslint ./src/ ./__tests__/; jscs ./src/ ./__tests__/",
"docs": "esdoc -c esdoc.json; open docs/index.html",
"deploy-docs": "node ./dist/uploader.js"
"test": "./utils/scripts/test",
"lint": "./utils/scripts/lint",
"lint:all": "./utils/scripts/lint-all",
"docs": "./utils/scripts/docs"
},
"jest": {
"collectCoverage": true,
"collectCoverageOnlyFrom": {
"src/index.js": true
},
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"testFileExtensions": ["es6", "js", "jsx"],
"moduleFileExtensions": ["js", "json", "jsx", "es6"],
"unmockedModulePathPatterns": [
"<rootDir>/src",
"<rootDir>/__tests__/utils",
"<rootDir>/node_modules/"
]
},
"bin": {
"esdoc-uploader": "./dist/uploader.js"
"esdoc-uploader": "./src/uploader.js"
},
"main": "dist/index.js"
"main": "./src/index.js",
"husky": {
"hooks": {
"pre-commit": "./utils/hooks/pre-commit",
"post-merge": "./utils/hooks/post-merge"
}
}
}

@@ -5,8 +5,26 @@ # esdoc-uploader

[![Build Status](https://travis-ci.org/homer0/esdoc-uploader.svg?branch=master)](https://travis-ci.org/homer0/esdoc-uploader) [![Coverage Status](https://coveralls.io/repos/homer0/esdoc-uploader/badge.svg?branch=master&service=github)](https://coveralls.io/github/homer0/esdoc-uploader?branch=master) [![Documentation Status](https://doc.esdoc.org/github.com/homer0/esdoc-uploader/badge.svg)](https://doc.esdoc.org/github.com/homer0/esdoc-uploader/) [![Dependencies status](https://david-dm.org/homer0/esdoc-uploader.svg)](https://david-dm.org/homer0/esdoc-uploader) [![Dev dependencies status](https://david-dm.org/homer0/esdoc-uploader/dev-status.svg)](https://david-dm.org/homer0/esdoc-uploader#info=devDependencies)
[![Travis](https://img.shields.io/travis/homer0/esdoc-uploader.svg?style=flat-square)](https://travis-ci.org/homer0/esdoc-uploader)
[![Coveralls github](https://img.shields.io/coveralls/github/homer0/esdoc-uploader.svg?style=flat-square)](https://coveralls.io/github/homer0/esdoc-uploader?branch=master)
[![David](https://img.shields.io/david/homer0/esdoc-uploader.svg?style=flat-square)](https://david-dm.org/homer0/esdoc-uploader)
[![David](https://img.shields.io/david/dev/homer0/esdoc-uploader.svg?style=flat-square)](https://david-dm.org/homer0/esdoc-uploader)
I've been using [ESDoc](https://esdoc.org) for a while now, and something great about it it's that they provide a [hosting service](https://doc.esdoc.org/) for you documentation. You only need to have your project hosted on GitHub and provide them with its url, the service will take care of cloning your repo, finding your `esdoc.json` file, generating the docs and publishing them, which I think it's pretty awesome!
I've been using [ESDoc](https://esdoc.org) for a while now, and something great about it it's that they provide a [hosting service](https://doc.esdoc.org/) for your documentation. You only need to have your project hosted on GitHub and give them with its url, the service will take care of cloning your repo, finding your `esdoc.json` file, generating the docs and publishing them, which I think it's pretty awesome!
Now, the only complication it's that every time you deploy a new change, you have to go to the page and submit a form with your repo url; but if you are working with **continuous integration**, doing that manually kind of kills the whole idea :P. and that's the reason of this project.
Now, the only complication it's that every time you deploy a new change, you have to go to the page and submit a form with your repo url; but if you are working with **continuous integration**, doing that manually kind of kills the whole idea :P... and that's the reason of this project.
> **Disclaimer (2019):** This project is still maintained, but there's no activity because the ESDoc hosting API doesn't have any other functionality, so no features will be added.
>
> If you are wondering why this project doesn't "use itself", it's because I no longer see the need to transpile Node code, and I can't use `esdoc-node` on the ESDoc hosting,
> so I'm using git pages.
>
> If you read the code... I know:
>
> 1. It should use promises.
> 2. It should separate the functionality on different modules.
> 3. I can use `node-fetch` or `axios` for the requests.
> 4. I can use `colors` or `chalk` for the log messages.
> 5. and so many more things...
>
> This project was one of my first npm packages, its scope is very limited, and when I updated it, I didn't want to completely rewrite it, just improve little details (what I could) while removing the need for production dependencies.
## Information

@@ -18,10 +36,4 @@

| Description | Upload your ESDoc documentation to doc.esdoc.org |
| Node Version | >= v0.12.6 (You need >= v4.0.0 for the tests) |
| Node Version | >= v8.10 |
## Installation
You can install it using [npm](https://www.npmjs.com/).
npm install esdoc-uploader --save_dev
## Usage

@@ -31,3 +43,7 @@

$(npm bin)/esdoc-uploader
```bash
npx esdoc-uploader
# or
yarn esdoc-uploader
```

@@ -47,11 +63,11 @@ That's all, `esdoc-uploader` will automatically look up your `package.json`, get your repository information and start the process.

if (uploader.canUpload()) {
uploader.upload(function(success, url) {
// Checks whether the process ended in success
if (success) {
// Logs a confirmation
console.log('Documents uploaded to: ', url);
} else {
console.log('Something went wrong, check the errors above');
}
});
uploader.upload((success, url) => {
// Checks whether the process ended in success
if (success) {
// Logs a confirmation
console.log('Documents uploaded to: ', url);
} else {
console.log('Something went wrong, check the errors above');
}
});
}

@@ -62,22 +78,35 @@ ```

- The `constructor`, which receives an already formatted GitHub url. Or you can ignore the argument and it will work like on the command line, looking for the information in your `package.json`.
- The `constructor`, which receives an already formatted GitHub url. Or you can ignore the argument and it will work just like on the command line, looking for the information in your `package.json`.
- `canUpload()`: It checks if the upload process can be done or not.
- `upload()`: It starts uploading everything to the API. It receives a callback parameter, which will be called when the process finishes. The callback will receive two arguments: a `boolean` value to check if the process was successful, and in case it was, the url for where the documentation it's being hosted.
- `upload()`: It starts uploading everything to the API. It receives a callback parameter, which will be called when the process finishes. The callback will then receive two arguments: a `boolean` value to check if the process was successful, and in case it was, the url for where the documentation it's being hosted.
## Development
### Install Git hooks
### NPM/Yarn tasks
./hooks/install
| Task | Description |
|------------|-------------------------------------|
| `test` | Run the project unit tests. |
| `lint` | Lint the modified files. |
| `lint:all` | Lint the entire project code. |
| `docs` | Generate the project documentation. |
### npm tasks
### Repository hooks
- `npm run build`: Generate a new build of the module.
- `npm test`: Run the module's unit tests.
- `npm run coverage`: Run the unit tests and open the coverage report on the browser.
- `npm run lint`: Lint the plugin's code with JSCS and ESLint.
- `npm run docs`: Generate the project documentation.
I use [husky](https://yarnpkg.com/en/package/husky) to automatically install the repository hooks so the code will be tested and linted before any commit and the dependencies updated after every merge. The configuration is on the `husky` property of the `package.json` and the hooks' files are on `./utils/hooks`.
### Testing
I use [Jest](https://facebook.github.io/jest/) with [Jest-Ex](https://yarnpkg.com/en/package/jest-ex) to test the project. The configuration file is on `./.jestrc.json`, the tests are on `./tests` and the script that runs it is on `./utils/scripts/test`.
### Linting
I use [ESlint](http://eslint.org) with [my own custom configuration](http://yarnpkg.com/en/package/eslint-plugin-homer0) to validate all the JS code. The configuration file for the project code is on `./.eslintrc` and the one for the tests is on `./tests/.eslintrc`. There's also an `./.eslintignore` to exclude some files on the process. The script that runs it is on `./utils/scripts/lint`.
### Documentation
I use [ESDoc](http://esdoc.org) (:P) to generate HTML documentation for the project. The configuration file is on `./.esdoc.json` and the script that runs it is on `./utils/scripts/docs`.
## License
MIT. [License file](./LICENSE).
MIT. [License file](./LICENSE).

@@ -0,414 +1,522 @@

const https = require('https');
const fs = require('fs');
const path = require('path');
/**
* @typedef {Function} UploadCallback
* @param {Boolean} success Whether the documentation was uploaded or not.
* @param {?String} url The url for the documentation.
*/
import fs from 'fs';
import path from 'path';
import request from 'request';
import logger from 'log-util';
/**
* ESDocUploader, connects with the [ESDoc hosting service](https://doc.esdoc.org/) API in order
* to generage the documentation for your project.
* @version 1.0.0
* @typedef {Function} RequestCallback
* @param {?Error} error In case the request fails.
* @param {String} response The request response.
* @param {Number} status The response status code.
* @ignore
*/
export default class ESDocUploader {
/**
* ESDocUploader, connects with the [ESDoc hosting service](https://doc.esdoc.org/) API in order
* to generage the documentation for your project.
*/
class ESDocUploader {
/**
* @param {?String} [url=null] This is the GitHub repository url. The required format its
* `git[at]github.com:[author]/[repository].git`. You can also
* ignore it and it will automatically search for it on your
* `package.json`.
*/
constructor(url = null) {
/**
* Create a new instance of the uploader.
* @param {String} [url=null] - This is the GitHub repository url. The required format its
* `git@github.com:[author]/[repository].git`. You can also
* ignore it and it will automatically search for it on your
* `package.json`.
* @public
* A list of pre defined messages that the class will log.
* @type {Object}
* @protected
* @ignore
*/
constructor(url = null) {
/**
* A list of pre defined messages that the class will log.
* @type {Object}
* @private
* @ignore
*/
this._messages = {
constructor: 'The repository url is invalid. ' +
'There is likely additional logging output above',
invalidUrl: 'The repository url is invalid. You can\'t upload anything',
uploading: 'The documentation is already being uploaded',
unexpected: 'Unexpected error, please try again',
noPackage: 'There\'s no package.json in this directory',
noRepository: 'There\'s no repository information in the package.json',
invalidFormat: 'The repository from the package.json it\'s not valid. ' +
'Expected format "[author]/[repository]"',
onlyGitHub: 'ESDoc only supports GitHub repositories',
success: 'The documentation was successfully uploaded:',
};
if (url === null) {
url = this._retrieveUrlFromPackage();
} else {
url = this._validateUrl(url);
}
/**
* The repository url. It can be null if the one provided is not valid or if there isn't
* one on the `package.json`.
* @type {string|null}
* @private
* @ignore
*/
this.url = url;
/**
* A flag to know if the class it's currently uploading something.
* @type {Boolean}
* @private
* @ignore
*/
this._uploading = false;
/**
* A small dictionary used to store information relative to the ESDoc API, like it's
* main domain or the path to create a new doc.
* @type {Object}
* @private
* @ignore
*/
this._api = {
domain: 'https://doc.esdoc.org',
create: '/api/create',
};
/**
* The name of the file where the class it's going to check if the docs were uploaded.
* @type {String}
* @private
* @ignore
*/
this._finishFile = '/.finish.json';
/**
* The amount of time the class will wait between checks to see if the docs site was
* generated.
* @type {Number}
* @private
* @ignore
*/
this._intervalTime = 4000;
/**
* After the first request, this is where the returned path for the docs on the server
* will be stored.
* @type {String}
* @private
* @ignore
*/
this._path = '';
/**
* A callback that will be executed after confirmation that the docs were generated.
* @type {Function}
* @private
* @ignore
*/
this._callback = null;
/**
* The text that will show up on the terminal.
* @type {String}
* @private
* @ignore
*/
this._indicatorText = 'Uploading';
/**
* The amout of time in which the indicator will be updated.
* @type {Number}
* @private
* @ignore
*/
this._indicatorInterval = 1000;
/**
* A utility counter to know how many dos will be added to the indicator
* @type {Number}
* @private
* @ignore
*/
this._indicatorCounter = -1;
/**
* After this many iterations, the dots will start to be removed instead of added. When the
* counter hits 0, it will start adding again, until it hits this limit.
* @type {Number}
* @private
* @ignore
*/
this._indicatorLimit = 3;
/**
* A flag to know if the indicator it's currently adding dots or removing them.
* @type {Boolean}
* @private
* @ignore
*/
this._indicatorIncrease = true;
/**
* If there's no url, log the error message.
*/
if (this.url === null) {
this._logError('constructor');
}
}
this._messages = {
invalidUrl: 'The repository url is invalid',
invalidPackageUrl: 'The repository url is invalid. ' +
'There is likely additional logging output above',
uploading: 'The documentation is already being uploaded',
unexpected: 'Unexpected error, please try again',
noPackage: 'There\'s no package.json in this directory',
noRepository: 'There\'s no repository information in the package.json',
invalidFormat: 'The repository from the package.json it\'s not valid. ' +
'Expected format "[author]/[repository]"',
onlyGithub: 'ESDoc only supports Github repositories',
success: 'The documentation was successfully uploaded:',
};
/**
* After the class is istantiated, this method can be used to check if the url is valid and
* if the method `upload` can be called
* @public
* The repository url. It can be `null` if the one provided via the parameter is invalid or
* if a valid one can't be retrieved from the `package.json`.
* @type {?String}
* @protected
* @ignore
*/
canUpload() {
return this.url !== null;
}
this._url = url === null ? this._retrieveUrlFromPackage() : this._validateUrl(url);
/**
* Upload your documentation to the ESDoc API.
* @param {Function} [callback=() => {}] - An optional callback to be executed after
* everthing is ready.
* @public
* A flag to know if the class it's currently uploading the documentation or not.
* @type {Boolean}
* @protected
* @ignore
*/
upload(callback = () => {}) {
if (this.url === null) {
this._callback = callback;
this._logError('invalidUrl');
} else if (this._uploading) {
this._logError('uploading');
} else {
this._callback = callback;
this._uploading = true;
this._startIndicator();
request.post({
url: this._getAPIUrl('create'),
body: {gitUrl: this.url},
json: true,
}, ((err, httpResponse, body) => {
if (err) {
this._logError(err);
} else {
let response = body;
if (typeof response === 'string') {
response = JSON.parse(response);
}
if (!response.success) {
this._logError(response.message || 'unexpected');
} else {
this._setAPIUrl('path', response.path);
this._setAPIUrl('status', response.path + this._finishFile);
this._startAsking();
}
}
}).bind(this));
}
}
this._uploading = false;
/**
* Tries to retrieve the repository url from your `pacakge.json`.
* @return {String} The repository url that was on your `package.json`.
* @private
* A small dictionary used to store information relative to the ESDoc API, like it's
* main hostname and the path to create a new documentation.
* When a new documentation is created, this object will be updated with the path
* to check if the documentation is ready.
* @type {Object}
* @protected
* @ignore
*/
_retrieveUrlFromPackage() {
const packagePath = path.resolve('./package.json');
const packageContents = fs.readFileSync(packagePath, 'utf-8');
let result = null;
if (!packageContents) {
this._logError('noPackage');
} else {
const property = JSON.parse(packageContents).repository;
if (!property) {
this._logError('noRepository');
}else if (typeof property === 'string') {
const urlParts = property.split('/');
if (urlParts.length !== 2) {
this._logError('invalidFormat');
} else {
result = this._buildUrl(urlParts[0], urlParts[1]);
}
} else {
if (property.type !== 'git' || !property.url.match(/github/)) {
this._logError('onlyGitHub');
} else {
const urlParts = property.url.split('/');
const author = urlParts[urlParts.length - 2];
const repository = urlParts[urlParts.length - 1];
result = this._buildUrl(author, repository);
}
}
}
return result;
}
this._api = {
host: 'doc.esdoc.org',
create: '/api/create',
};
/**
* Generates a new url with the required format to use with the ESDoc API.
* @param {String} author - The GitHub username.
* @param {String} repository - The repository name.
* @return {String} The new url, on the required format for ESDoc.
* @private
* The name of the file where the class it's going to check if the docs were uploaded. The
* complete path is created with the information from the response the class gets when a
* new documentation is created.
* @type {String}
* @protected
* @ignore
*/
_buildUrl(author, repository) {
if (repository.indexOf('.git') > -1) {
repository = repository.substr(0, repository.length - 4);
}
return 'git@github.com:' + author + '/' + repository + '.git';
}
this._finishFile = '/.finish.json';
/**
* Validates a given url to see if it has the required format by the ESDoc API.
* @param {String} url - The url to validate.
* @return {String|null} If the url it's valid, it will return it, otherwise, itw will
* return null.
* @private
* The interval time the class will use in order to check if an uploaded documentation
* is available or not.
* @type {Number}
* @protected
* @ignore
*/
_validateUrl(url) {
let result = null;
if (url.match(/^git@github\.com:[\w\d._-]+\/[\w\d._-]+\.git$/)) {
result = url;
}
return result;
}
this._intervalTime = 4000;
/**
* This method is called after the initial request to the API, and tells the class to check
* every X seconds to see if the documentation was uploaded.
* @private
* A callback that will be executed after getting a confirmation that the documentation
* was successfully updated. It's value is set using the `upload` method.
* @type {?UploadCallback}
* @protected
* @ignore
*/
_startAsking() {
setTimeout(this._ask.bind(this), this._intervalTime);
}
this._callback = null;
/**
* It makes a request to check if the documentation was uploaded or not.
* @private
* The text that will show up on the console.
* @type {String}
* @protected
* @ignore
*/
_ask() {
request(this._getAPIUrl('status'), ((err, httpResponse, body) => {
if (err || body.indexOf('<html>') > -1) {
this._startAsking();
} else {
const response = JSON.parse(body);
if (!response.success) {
this._logError(response.message || 'unexpected');
} else {
this._finish();
}
}
}).bind(this));
}
this._indicatorText = 'Uploading';
/**
* This method is called after it's confirmed that the documentation was successfully uploaded,
* and it stops teh indicator, logs a mesage with the url for the documetation and invokes the
* callback set in the `upload()` method.
* @private
* The amout of time in which the indicator will be updated.
* @type {Number}
* @protected
* @ignore
*/
_finish() {
this._uploading = false;
this._stopIndicator();
const docUrl = this._getAPIUrl('path');
logger.debug(this._messages.success + ' ' + docUrl);
this._callback(true, docUrl);
}
this._indicatorInterval = 1000;
/**
* Returns a url for the ESDoc API.
* @param {String} type - The type of url you need. This is parameter it's the key for the
* `_api` dictionary.
* @return {String} It will return the API domain and the value in the `_api` dictionary for
* given type.
* @private
* A utility counter to know how many dots will be added to the indicator.
* @type {Number}
* @protected
* @ignore
*/
_getAPIUrl(type) {
return this._api.domain + this._api[type];
}
this._indicatorCounter = -1;
/**
* Set a new type of urlf or the ESDoc API. For example, the first request will return a
* relative path for the documentation, this class will use this method to save this path
* so it can be later be retrieved using `_getAPIUrl` and it wil already have the API main
* domain.
* @param {String} type - An identifier for your url.
* @param {String} url - The relative url you want to save.
* @private
* After this many iterations, the dots in the indicator will start to be removed instead
* of being added. When the counter hits 0, it will start adding again, until it
* hits this limit.
* @type {Number}
* @protected
* @ignore
*/
_setAPIUrl(type, url) {
this._api[type] = url;
}
this._indicatorLimit = 3;
/**
* Logs an eror message to the terminal.
* @param {String|Error} error - This can be the message you want to log, a key for the
* `_messages` dictionary or an `Error` object.
* @private
* A flag to know if the indicator it's currently adding dots or removing them.
* @type {Boolean}
* @protected
* @ignore
*/
_logError(error) {
if (typeof error === 'string') {
if (this._messages[error]) {
error = this._messages[error];
}
} else {
error = error.message;
}
logger.error(error);
this._stopIndicator(false);
if (this._callback) {
this._callback(false);
}
}
this._indicatorIncrease = true;
/**
* Starts showing the progress indicator on the terminal.
* @private
* @ignore
*/
_startIndicator() {
this._indicatorInterval = setInterval(this._runIndicator.bind(this), 500);
}
this._ask = this._ask.bind(this);
/**
* The actual method that shows the progress indicator on the terminal.
* @private
* @ignore
*/
_runIndicator() {
let text = this._indicatorText;
if (this._indicatorIncrease) {
this._indicatorCounter++;
if (this._indicatorCounter === this._indicatorLimit) {
this._indicatorIncrease = false;
this._runIndicator = this._runIndicator.bind(this);
}
/**
* Checks whether the repository is valid and the class can start uploading the documentation.
* @return {Boolean}
*/
canUpload() {
return !!this._url;
}
/**
* Upload your documentation to the ESDoc API.
* @param {UploadCallback} callback An optional callback to be executed after everthing
* is ready.
*/
upload(callback) {
if (this._url === null) {
this._callback = callback;
this._logError('invalidUrl');
} else if (this._uploading) {
this._logError('uploading');
} else {
this._callback = callback;
this._uploading = true;
this._startIndicator();
this._postRequest(
'create',
{ gitUrl: this._url },
(error, response) => {
if (error) {
this._logError(error);
} else {
const useResponse = JSON.parse(response);
if (useResponse.success) {
this._setAPIPath('path', useResponse.path);
this._setAPIPath(
'status',
`${useResponse.path}${this._finishFile}`
);
this._startAsking();
} else {
this._logError(useResponse.message || 'unexpected');
}
}
}
);
}
}
/**
* The repository url the class will send to the ESDoc API.
* @type {?String}
*/
get url() {
return this._url;
}
/**
* Tries to retrieve the repository url from the project's `pacakge.json`.
* @return {String}
* @protected
* @ignore
*/
_retrieveUrlFromPackage() {
const packagePath = path.resolve('./package.json');
let packageContents;
try {
packageContents = fs.readFileSync(packagePath, 'utf-8');
} catch (ignore) {
// This is ignored because we already have the error going out if there's no package.
}
let result = null;
if (packageContents) {
const authorAndRepoParts = 2;
const property = JSON.parse(packageContents).repository;
if (!property) {
this._logError('noRepository');
} else if (typeof property === 'string') {
const urlParts = property.split('/');
if (urlParts.length !== authorAndRepoParts) {
this._logError('invalidFormat');
} else {
this._indicatorCounter--;
if (this._indicatorCounter === 0) {
this._indicatorIncrease = true;
}
result = this._buildUrl(urlParts[0], urlParts[1]);
}
} else if (property.type !== 'git' || !property.url.match(/github/)) {
this._logError('onlyGithub');
} else {
const urlParts = property.url.split('/');
const author = urlParts[urlParts.length - authorAndRepoParts];
const repository = urlParts[urlParts.length - 1];
result = this._buildUrl(author, repository);
}
} else {
this._logError('noPackage');
}
for (let i = 0; i < this._indicatorCounter; i++) {
text += '.';
}
if (result === null) {
this._logError('invalidPackageUrl');
}
this._restartLine();
this._print(text);
return result;
}
/**
* Generates a new url with the required format to use with the ESDoc API.
* @param {String} author The GitHub username.
* @param {String} repository The repository name.
* @return {String}
* @protected
* @ignore
*/
_buildUrl(author, repository) {
const extension = '.git';
const useRepository = repository.includes(extension) ?
repository.substr(0, repository.length - extension.length) :
repository;
return `git@github.com:${author}/${useRepository}.git`;
}
/**
* Validates a given url to see if it has the required format by the ESDoc API.
* @param {String} url - The url to validate.
* @return {?String} If the url it's valid, it will return it, otherwise, it will
* return `null`.
* @protected
* @ignore
*/
_validateUrl(url) {
let result = null;
if (url.match(/^git@github\.com:[\w\d._-]+\/[\w\d._-]+\.git$/)) {
result = url;
} else {
this._logError('invalidUrl');
}
/**
* Removes the progress indicator from the terminal.
* @private
* @ignore
*/
_stopIndicator() {
clearInterval(this._indicatorInterval);
this._restartLine();
return result;
}
/**
* This method is called after the initial request to the API, and tells the class to check
* every X milliseconds to see if the documentation was uploaded.
* @protected
* @ignore
*/
_startAsking() {
setTimeout(this._ask, this._intervalTime);
}
/**
* It makes a request to check if the documentation was uploaded or not. If is not ready, it
* will call `_startAsking` to setup a new check; otherwise, it will invoke the callback sent
* to `upload`.
* @protected
* @ignore
*/
_ask() {
this._getRequest('status', (error, response) => {
if (error || response.includes('<html>')) {
this._startAsking();
} else {
const useResponse = JSON.parse(response);
if (useResponse.success) {
this._finish();
} else {
this._logError(useResponse.message || 'unexpected');
}
}
});
}
/**
* This method is called after it's confirmed that the documentation was successfully uploaded,
* it stops the indicator, logs a message with the url for the documetation and invokes the
* callback set in the `upload()` method.
* @protected
* @ignore
*/
_finish() {
this._uploading = false;
this._stopIndicator();
const url = this._getAPIUrl('path');
// eslint-disable-next-line no-console
console.log(
'\x1b[30m[%s] \x1b[32m%s\x1b[0m',
new Date(),
`${this._messages.success} ${url}`
);
this._callback(true, url);
}
/**
* Returns a complete url for the ESDoc API.
* @param {String} apiPath The reference name for the path the request is for,
* inside the `_api` dictionary.
* @protected
* @ignore
*/
_getAPIUrl(apiPath) {
const usePath = this._api[apiPath];
return `https://${this._api.host}${usePath}`;
}
/**
* Makes a POST request to the API.
* @param {String} apiPath The reference name for the path the request is for,
* inside the `_api` dictionary.
* @param {Object} body The body of the request.
* @param {RequestCallback} callback The callback to be invoked when the request is finished.
* @protected
* @ignore
*/
_postRequest(apiPath, body, callback) {
const data = JSON.stringify(body);
const options = {
hostname: this._api.host,
path: this._api[apiPath],
port: 443,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const req = this._createAPIRequest(options, callback);
req.write(data);
req.end();
}
/**
* Makes a GET request to the API.
* @param {String} apiPath The reference name for the path the request is for,
* inside the `_api` dictionary.
* @param {RequestCallback} callback The callback to be invoked when the request is finished.
* @protected
* @ignore
*/
_getRequest(apiPath, callback) {
const options = {
hostname: this._api.host,
path: this._api[apiPath],
port: 443,
method: 'GET',
};
const req = this._createAPIRequest(options, callback);
req.end();
}
/**
* A wrapper on top of `https.request` that allows the class to make requests, setup the
* listeners and resolve everything on a single callback.
* @param {Object} reqOptions The options for `https.request`.
* @param {RequestCallback} callback The callback to be invoked when the request is
* finished.
* @protected
* @ignore
*/
_createAPIRequest(reqOptions, callback) {
return https.request(reqOptions, (res) => {
const { statusCode } = res;
const chunks = [];
let errored = false;
res.on('data', (chunk) => {
chunks.push(chunk);
});
res.on('error', (error) => {
errored = true;
callback(error, null, statusCode);
});
res.on('end', () => {
if (!errored) {
const response = Buffer.concat(chunks).toString();
const badRequest = 400;
if (statusCode >= badRequest) {
callback(
new Error(`The API responded with a ${statusCode}`),
response,
statusCode
);
} else {
callback(null, response, statusCode);
}
}
});
});
}
/**
* Sets a new path reference to be used with the ESDoc API.
* After triggering the upload, this will be used to store the path the API uses so the class
* can check if the documentation is available.
* @param {String} name A reference identifier for the path.
* @param {String} apiPath The relative path you want to save.
* @protected
* @ignore
*/
_setAPIPath(name, apiPath) {
this._api[name] = apiPath;
}
/**
* Logs an eror message to the terminal and, if `upload` was ever call, it invokes the callback
* informing that the operation wasn't successful.
* @param {String|Error} error The message to log, a key for the `_messages` dictionary or
* an `Error` object.
* @protected
* @ignore
*/
_logError(error) {
let useError;
if (typeof error === 'string') {
useError = this._messages[error] || error;
} else {
useError = error.message;
}
/**
* Removes everything on the current terminal line and sets the cursor to the initial
* position.
* @private
* @ignore
*/
_restartLine() {
process.stdout.clearLine();
process.stdout.cursorTo(0);
// eslint-disable-next-line no-console
console.log('\x1b[30m[%s] \x1b[31m%s\x1b[0m', new Date(), useError);
this._stopIndicator(false);
if (this._callback) {
this._callback(false);
}
/**
* Writes a message in the terminal.
* @param {String} message - The text to write.
* @private
* @ignore
*/
_print(message) {
process.stdout.write(message);
}
/**
* Starts showing the progress indicator on the terminal.
* @protected
* @ignore
*/
_startIndicator() {
const indicatorIntervalTime = 500;
this._indicatorInterval = setInterval(
this._runIndicator,
indicatorIntervalTime
);
}
/**
* The actual method that shows the progress indicator on the terminal.
* @protected
* @ignore
*/
_runIndicator() {
let text = this._indicatorText;
if (this._indicatorIncrease) {
this._indicatorCounter++;
if (this._indicatorCounter === this._indicatorLimit) {
this._indicatorIncrease = false;
}
} else {
this._indicatorCounter--;
if (this._indicatorCounter === 0) {
this._indicatorIncrease = true;
}
}
for (let i = 0; i < this._indicatorCounter; i++) {
text += '.';
}
this._restartLine();
this._print(text);
}
/**
* Removes the progress indicator from the terminal.
* @protected
* @ignore
*/
_stopIndicator() {
clearInterval(this._indicatorInterval);
this._restartLine();
}
/**
* Removes everything on the current terminal line and sets the cursor to the initial
* position.
* @protected
* @ignore
*/
_restartLine() {
process.stdout.clearLine();
process.stdout.cursorTo(0);
}
/**
* Writes a message in the terminal.
* @param {String} message - The text to write.
* @protected
* @ignore
*/
_print(message) {
process.stdout.write(message);
}
}
module.exports = ESDocUploader;
#!/usr/bin/env node
'use strict';
// esdoc-uploader: CLI interface
// Import the module's main class
import ESDocUploader from './index';
const ESDocUploader = require('./index');
// Instantiate an object that will detect the url from your package.json

@@ -12,4 +10,4 @@ const uploader = new ESDocUploader();

if (uploader.canUpload()) {
// Start uploading the docs
uploader.upload();
// Start uploading the docs
uploader.upload();
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet