Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add jsdelivr asset manager #46

Merged
merged 1 commit into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ In that case, Bmyc should just fit your needs.
* [github](https://github.com/) (Sources from repository)
* [cdnjs](https://cdnjs.com/)
* [unpkg](https://unpkg.com/)
* [jsdelivr](https://www.jsdelivr.com/)

## How to use it?

Expand Down Expand Up @@ -52,6 +53,12 @@ Examples:
bmyc --config "./myconfig.json" Use specific configuration file
bmyc --summary-pr "./summary-pr.md" Generate markdown summary results to describe a Pull Request

Package managers available:
- cdnjs
- github
- unpkg
- jsdelivr

Additional information:
GitHub: https://github.com/jgazeau/bmyc.git
Documentation: https://github.com/jgazeau/bmyc#readme
Expand Down
15 changes: 14 additions & 1 deletion src/cli/bmycCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import kleur = require('kleur');
import {hideBin} from 'yargs/helpers';
import {getOutputWidth} from '../utils/helpers';
import {IArgumentsParser, ICliArguments} from './iArgumentsParser';
import {AssetManagerType} from '../model/assetManagers/assetManagerType';
import {
CLI_USAGE,
CONFIG_OPTION,
Expand Down Expand Up @@ -92,7 +93,9 @@ export class BmycCli {
})
.wrap(getOutputWidth())
.epilog(
`Additional information:
`Package managers available:
${this.printAvailablePackages(2)}
Additional information:
GitHub: ${kleur.green('https://github.com/jgazeau/bmyc.git')}
Documentation: ${kleur.blue('https://github.com/jgazeau/bmyc#readme')}
Issues: ${kleur.red('https://github.com/jgazeau/bmyc/issues')}
Expand All @@ -104,4 +107,14 @@ export class BmycCli {
this.parser.argv;
return Promise.resolve(BmycCli.cliArgs);
}

private printAvailablePackages(offset: number): string {
let availablePackages = '';
Object.values(AssetManagerType).forEach(entry => {
availablePackages = `${availablePackages}${' '.repeat(
offset
)}- ${entry}\n`;
});
return availablePackages;
}
}
1 change: 1 addition & 0 deletions src/model/assetManagers/assetManagerType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum AssetManagerType {
CDNJS = 'cdnjs',
GITHUB = 'github',
UNPKG = 'unpkg',
JSDELIVR = 'jsdelivr',
}
70 changes: 70 additions & 0 deletions src/model/assetManagers/jsdelivr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* eslint-disable @typescript-eslint/no-explicit-any*/
import axios from 'axios';
import {AssetManager} from './assetManager';
import {IsDefined, IsString} from 'class-validator';
import {unknownLatestVersionError} from '../bmycError';

const JSDELIVR_API_URL = 'https://data.jsdelivr.com/v1';
const JSDELIVR_DATA_URL = 'https://cdn.jsdelivr.net';
const JSDELIVR_PACKAGE_PATH = 'package';

export class Jsdelivr extends AssetManager {
@IsDefined()
@IsString()
private cdn: string;
/* c8 ignore start */
public get _cdn(): string {
return this.cdn;
}
public set _cdn(value: string) {
this.cdn = value;
}
/* c8 ignore stop */

@IsDefined()
@IsString()
private package: string;
/* c8 ignore start */
public get _package(): string {
return this.package;
}
public set _package(value: string) {
this.package = value;
}
/* c8 ignore stop */

@IsDefined()
@IsString()
private filePath: string;
/* c8 ignore start */
public get _filePath(): string {
return this.filePath;
}
public set _filePath(value: string) {
this.filePath = value;
}
/* c8 ignore stop */

getLatestVersion(): Promise<string> {
return axios({
method: 'get',
url: `${JSDELIVR_API_URL}/${JSDELIVR_PACKAGE_PATH}/${this.cdn}/${this.package}`,
}).then((response: any) => {
const version = response.data.tags.latest;
if (version) {
return Promise.resolve(version);
} else {
throw unknownLatestVersionError(`${this.cdn}/${this.package}`);
}
});
}

getContent(assetVersion: string): Promise<Buffer> {
return axios({
method: 'get',
url: `${JSDELIVR_DATA_URL}/${this.cdn}/${this.package}@${assetVersion}/${this.filePath}`,
}).then((response: any) => {
return Promise.resolve(response.data);
});
}
}
6 changes: 3 additions & 3 deletions src/model/assetManagers/unpkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {AssetManager} from './assetManager';
import {IsDefined, IsString} from 'class-validator';
import {unknownLatestVersionError} from '../bmycError';

const UNPKG_URL = 'https://unpkg.com';
const UNPKG_API_URL = 'https://unpkg.com';

export class Unpkg extends AssetManager {
@IsDefined()
Expand Down Expand Up @@ -34,7 +34,7 @@ export class Unpkg extends AssetManager {
getLatestVersion(): Promise<string> {
return axios({
method: 'get',
url: `${UNPKG_URL}/${this.library}/`,
url: `${UNPKG_API_URL}/${this.library}/`,
}).then((response: any) => {
// Unpkg should redirect to the latest version of the package by default
let version = response.request.res.responseUrl.replace(
Expand All @@ -53,7 +53,7 @@ export class Unpkg extends AssetManager {
getContent(assetVersion: string): Promise<Buffer> {
return axios({
method: 'get',
url: `${UNPKG_URL}/${this.library}@${assetVersion}/${this.filePath}`,
url: `${UNPKG_API_URL}/${this.library}@${assetVersion}/${this.filePath}`,
}).then((response: any) => {
return Promise.resolve(response.data);
});
Expand Down
2 changes: 2 additions & 0 deletions src/model/configurationFile/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Unpkg} from '../assetManagers/unpkg';
import {Cdnjs} from '../assetManagers/cdnjs';
import {Github} from '../assetManagers/github';
import {Exclude, Type} from 'class-transformer';
import {Jsdelivr} from '../assetManagers/jsdelivr';
import {AssetManagerType} from '../assetManagers/assetManagerType';
import {AssetManager as AssetManager} from '../assetManagers/assetManager';
import {AssetManagerValidator as AssetManagerValidator} from '../assetManagers/assetManagerValidator';
Expand Down Expand Up @@ -77,6 +78,7 @@ export class Asset {
{value: Cdnjs, name: AssetManagerType.CDNJS},
{value: Github, name: AssetManagerType.GITHUB},
{value: Unpkg, name: AssetManagerType.UNPKG},
{value: Jsdelivr, name: AssetManagerType.JSDELIVR},
],
},
})
Expand Down
Empty file.
Empty file.
134 changes: 134 additions & 0 deletions test/model/assetManager/test-jsdelivr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* eslint-disable @typescript-eslint/no-explicit-any*/
import 'reflect-metadata';
import * as path from 'path';
import * as fs from 'fs-extra';
import {expect} from 'chai';
import {PathLike} from 'fs-extra';
import {testResourcesPath} from '../../testUtils/const';
import {setChaiAsPromised} from '../../testUtils/helpers';
import {deserializeObject} from '../../../src/utils/helpers';
import {Jsdelivr} from '../../../src/model/assetManagers/jsdelivr';
import {ConfigurationError} from '../../../src/model/configurationError';

const JSDELIVR_SAMPLE_UNEXISTING_CDN: PathLike = path.join(
testResourcesPath,
'jsdelivr-sample-unexisting-cdn.json'
);
const JSDELIVR_SAMPLE_UNEXISTING_PACKAGE: PathLike = path.join(
testResourcesPath,
'jsdelivr-sample-unexisting-package.json'
);
const JSDELIVR_SAMPLE_UNEXISTING_FILE: PathLike = path.join(
testResourcesPath,
'jsdelivr-sample-unexisting-file.json'
);
const JSDELIVR_SAMPLE_VALID: PathLike = path.join(
testResourcesPath,
'jsdelivr-sample-valid.json'
);

describe('Jsdelivr AssetManager tests', () => {
it('Jsdelivr should implement getLatestVersion', () => {
const jsdelivr: Jsdelivr = new Jsdelivr();
expect(jsdelivr.getLatestVersion).to.be.instanceof(Function);
});
it('Jsdelivr should implement getContent', () => {
const jsdelivr: Jsdelivr = new Jsdelivr();
expect(jsdelivr.getContent).to.be.instanceof(Function);
});
it('Jsdelivr should throw a ConfigurationError when name not set', () => {
const tempInput: any = JSON.parse(
fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8')
);
delete tempInput.name;
const input: string = JSON.stringify(tempInput);
expect(() => {
deserializeObject(input, Jsdelivr);
}).to.throw(ConfigurationError);
});
it('Jsdelivr should throw a ConfigurationError when cdn not set', () => {
const tempInput: any = JSON.parse(
fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8')
);
delete tempInput.cdn;
const input: string = JSON.stringify(tempInput);
expect(() => {
deserializeObject(input, Jsdelivr);
}).to.throw(ConfigurationError);
});
it('Jsdelivr should throw a ConfigurationError when package not set', () => {
const tempInput: any = JSON.parse(
fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8')
);
delete tempInput.package;
const input: string = JSON.stringify(tempInput);
expect(() => {
deserializeObject(input, Jsdelivr);
}).to.throw(ConfigurationError);
});
it('Jsdelivr should throw a ConfigurationError when filePath not set', () => {
const tempInput: any = JSON.parse(
fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8')
);
delete tempInput.filePath;
const input: string = JSON.stringify(tempInput);
expect(() => {
deserializeObject(input, Jsdelivr);
}).to.throw(ConfigurationError);
});
it('getLatestVersion should return the latest version', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8');
const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr);
return expect(jsdelivr.getLatestVersion()).to.eventually.match(
/^[0-9]+\.[0-9]+\.[0-9]+([.-][a-zA-Z0-9]+)*$/
);
});
it('getLatestVersion should throw a Error when unexisting cdn', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(
JSDELIVR_SAMPLE_UNEXISTING_CDN,
'utf8'
);
const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr);
return expect(jsdelivr.getLatestVersion()).to.eventually.be.rejectedWith(
Error,
'400'
);
});
it('getLatestVersion should throw a Error when unexisting package', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(
JSDELIVR_SAMPLE_UNEXISTING_PACKAGE,
'utf8'
);
const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr);
return expect(jsdelivr.getLatestVersion()).to.eventually.be.rejectedWith(
Error,
'404'
);
});
it('getContent should return the latest content', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8');
const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr);
return jsdelivr.getLatestVersion().then(latestVersion => {
return jsdelivr.getContent(latestVersion).then(content => {
expect(content).to.not.be.empty;
});
});
});
it('getContent should throw a Error when unexisting file', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(
JSDELIVR_SAMPLE_UNEXISTING_FILE,
'utf8'
);
const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr);
return jsdelivr.getLatestVersion().then(latestVersion => {
return expect(
jsdelivr.getContent(latestVersion)
).to.eventually.be.rejectedWith(Error, '404');
});
});
});
2 changes: 1 addition & 1 deletion test/model/assetManager/test-unpkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('Unpkg AssetManager tests', () => {
});
});
});
it('getContent should throw a BmycError when unexisting file', () => {
it('getContent should throw a Error when unexisting file', () => {
setChaiAsPromised();
const input: string = fs.readFileSync(UNPKG_SAMPLE_UNEXISTING_FILE, 'utf8');
const unpkg: Unpkg = deserializeObject(input, Unpkg);
Expand Down
2 changes: 1 addition & 1 deletion test/resources/.bmycconfig-incorrect.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"localPath": "test/temp/bmyc-cdnjs-incorrect-unknown.min.js",
"assetManager": {
"name": "cdnjs",
"library": "typescript",
"library": "axios",
"fileName": "unknown.min.js"
}
},
Expand Down
22 changes: 16 additions & 6 deletions test/resources/.bmycconfig-specific.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[
{
"name": "asset1",
"localPath": "test/temp/bmyc-cdnjs-specific-typescript.min.js",
"localPath": "test/temp/bmyc-cdnjs-specific-axios.min.js",
"assetManager": {
"name": "cdnjs",
"library": "typescript",
"fileName": "typescript.min.js"
"library": "axios",
"fileName": "axios.min.js"
}
},
{
Expand All @@ -20,11 +20,21 @@
},
{
"name": "asset3",
"localPath": "test/temp/bmyc-unpkg-typescript-ok.js",
"localPath": "test/temp/bmyc-unpkg-axios-ok.js",
"assetManager": {
"name": "unpkg",
"library": "typescript",
"filePath": "bin/tsc"
"library": "axios",
"filePath": "dist/axios.min.js"
}
},
{
"name": "asset4",
"localPath": "test/temp/bmyc-jsdelivr-axios-ok.js",
"assetManager": {
"name": "jsdelivr",
"cdn": "npm",
"package": "axios",
"filePath": "dist/axios.min.js"
}
}
]
Loading