diff --git a/.eslintignore b/.eslintignore index a1ef16e..c92fd8e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,4 @@ docs .idea .vscode *.d.ts +lib diff --git a/.eslintrc.js b/.eslintrc.js index 093d466..f271442 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,9 @@ module.exports = { + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint/eslint-plugin'], extends: [ 'alloy', + 'alloy/typescript', ], root: true, env: { @@ -8,7 +11,7 @@ module.exports = { jest: true, }, rules: { - semi: ['error', 'always'], + semi: 'off', 'comma-dangle': ['error', 'always-multiline'], 'switch-colon-spacing': ['error', { 'after': true, 'before': false }], quotes: ['error', 'single'], @@ -24,5 +27,29 @@ module.exports = { 'comma-spacing': ['error', { 'before': false, 'after': true }], 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true, 'mode': 'strict' }], 'no-multiple-empty-lines': ['error', { 'max': 1, 'maxEOF': 1 }], + '@typescript-eslint/semi': ['error', 'always'], + '@typescript-eslint/space-infix-ops': ['error', { 'int32Hint': true }], + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/consistent-type-assertions': 'off', + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/type-annotation-spacing': [ + 'error', + { + 'before': true, + 'after': true, + 'overrides': { + 'colon': { + 'before': false, + 'after': true, + }, + }, + }, + ], }, }; diff --git a/.gitignore b/.gitignore index e32d92f..bc91823 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ temp .versions .changelog package-lock.json +lib diff --git a/package.json b/package.json index 6b8d4f4..810d9b2 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,14 @@ { "private": true, "devDependencies": { + "@types/jest": "^26.0.23", + "@types/node": "^15.12.2", + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", "babel-eslint": "^10.1.0", "eslint": "^7.28.0", "eslint-config-alloy": "^4.1.0", + "jest": "^27.0.4", "lerna": "^4.0.0", "typescript": "^4.3.2" } diff --git a/packages/@dollie/cli/README.md b/packages/@dollie/cli/README.md new file mode 100644 index 0000000..38324fb --- /dev/null +++ b/packages/@dollie/cli/README.md @@ -0,0 +1,11 @@ +# `@dollie/cli` + +> TODO: description + +## Usage + +``` +const cli = require('@dollie/cli'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/@dollie/cli/package.json b/packages/@dollie/cli/package.json new file mode 100644 index 0000000..a534d01 --- /dev/null +++ b/packages/@dollie/cli/package.json @@ -0,0 +1,29 @@ +{ + "name": "@dollie/cli", + "version": "3.0.0", + "description": "Dollie CLI", + "author": "lenconda ", + "homepage": "https://github.com/lenconda/dollie#readme", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/lenconda/dollie.git" + }, + "bin": { + "dollie": "bin/dollie.js" + }, + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/lenconda/dollie/issues" + }, + "dependencies": { + "commander": "^7.2.0" + }, + "devDependencies": { + "@types/lodash": "^4.14.170", + "@types/node": "^15.12.2", + "typescript": "^4.3.2" + } +} diff --git a/packages/@dollie/cli/src/index.ts b/packages/@dollie/cli/src/index.ts new file mode 100644 index 0000000..b7bd4c8 --- /dev/null +++ b/packages/@dollie/cli/src/index.ts @@ -0,0 +1 @@ +console.log(); diff --git a/packages/@dollie/cli/tsconfig.json b/packages/@dollie/cli/tsconfig.json new file mode 100644 index 0000000..8d5aacc --- /dev/null +++ b/packages/@dollie/cli/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } +} diff --git a/packages/@dollie/core/lib/index.js b/packages/@dollie/core/lib/index.js deleted file mode 100644 index 061e11e..0000000 --- a/packages/@dollie/core/lib/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = core; - -function core() { - // TODO - console.log(); -} diff --git a/packages/@dollie/core/package.json b/packages/@dollie/core/package.json index b692bf3..884a52d 100644 --- a/packages/@dollie/core/package.json +++ b/packages/@dollie/core/package.json @@ -13,6 +13,7 @@ "scripts": { "test": "echo \"Error: run tests from root\" && exit 1" }, + "typings": "lib/index.d.ts", "bugs": { "url": "https://github.com/lenconda/dollie/issues" }, @@ -27,6 +28,8 @@ }, "devDependencies": { "@types/diff": "^5.0.0", - "@types/lodash": "^4.14.170" + "@types/lodash": "^4.14.170", + "@types/node": "^15.12.2", + "typescript": "^4.3.2" } } diff --git a/packages/@dollie/core/lib/compare.js b/packages/@dollie/core/src/compare.ts similarity index 87% rename from packages/@dollie/core/lib/compare.js rename to packages/@dollie/core/src/compare.ts index 65b43a1..60bb47b 100644 --- a/packages/@dollie/core/lib/compare.js +++ b/packages/@dollie/core/src/compare.ts @@ -1,9 +1,10 @@ -const { diffLines } = require('diff'); -const _ = require('lodash'); +import { diffLines } from 'diff'; +import _ from 'lodash'; +import { DiffChange, PatchTable } from './interfaces'; -function diff(originalContent, currentContent) { +const diff = (originalContent: string, currentContent: string): DiffChange[] => { const changes = diffLines(originalContent, currentContent || originalContent); - const result = []; + const result: DiffChange[] = []; let lineNumber = 0; const splitChanges = changes.reduce((result, currentItem) => { @@ -26,7 +27,7 @@ function diff(originalContent, currentContent) { return result; }; -function merge(originalChanges, diffList) { +const merge = (originalChanges: DiffChange[], diffList: DiffChange[][]): DiffChange[] => { if (!originalChanges) { return []; } @@ -36,7 +37,7 @@ function merge(originalChanges, diffList) { } const originalDiff = Array.from(originalChanges); - const patchTable = {}; + const patchTable: PatchTable = {}; for (const currentDiff of diffList) { for (const change of currentDiff) { @@ -74,7 +75,7 @@ function merge(originalChanges, diffList) { } } - const blocks = []; + const blocks: Array> = []; const patches = Object.keys(patchTable).map( (patchIndex) => patchTable[patchIndex], ); @@ -103,7 +104,7 @@ function merge(originalChanges, diffList) { }, []); }; -module.exports = { +export { diff, merge, }; diff --git a/packages/@dollie/core/lib/constants.js b/packages/@dollie/core/src/constants.ts similarity index 84% rename from packages/@dollie/core/lib/constants.js rename to packages/@dollie/core/src/constants.ts index 7e9422e..4388241 100644 --- a/packages/@dollie/core/lib/constants.js +++ b/packages/@dollie/core/src/constants.ts @@ -1,5 +1,5 @@ const VIRTUAL_VOLUME_DESTINATION_PATHNAME = '/template'; -module.exports = { +export { VIRTUAL_VOLUME_DESTINATION_PATHNAME, }; diff --git a/packages/@dollie/core/lib/errors.js b/packages/@dollie/core/src/errors.ts similarity index 63% rename from packages/@dollie/core/lib/errors.js rename to packages/@dollie/core/src/errors.ts index 1a2a555..433a8a3 100644 --- a/packages/@dollie/core/lib/errors.js +++ b/packages/@dollie/core/src/errors.ts @@ -1,32 +1,41 @@ -const _ = require('lodash'); +import _ from 'lodash'; class InvalidInputError extends Error { - constructor(reason) { + public constructor(reason) { super(`Invalid input item${_.isString(reason) ? `: ${reason}` : ''}`); } } class ContextError extends Error { - constructor(reason) { + public constructor(reason) { super(`Invalid context${_.isString(reason) ? `: ${reason}` : ''}`); } } class HTTPNotFoundError extends Error { - constructor() { + public constructor() { super('Template resource not found'); } } class HTTPTimeoutError extends Error { - constructor() { + public constructor() { super('Download template resource timed out'); } } -module.exports = { +class DollieError extends Error { + public code: string; + + public constructor(message: string) { + super(message); + } +} + +export { InvalidInputError, ContextError, HTTPNotFoundError, HTTPTimeoutError, + DollieError, }; diff --git a/packages/@dollie/core/lib/generator.js b/packages/@dollie/core/src/generator.ts similarity index 82% rename from packages/@dollie/core/lib/generator.js rename to packages/@dollie/core/src/generator.ts index 89617d7..6fd6e64 100644 --- a/packages/@dollie/core/lib/generator.js +++ b/packages/@dollie/core/src/generator.ts @@ -1,4 +1,6 @@ -const _ = require('lodash'); +import { DollieConfig } from './interfaces'; +import _ from 'lodash'; + const { InvalidInputError, ContextError, @@ -7,7 +9,7 @@ const { githubOrigin, gitlabOrigin } = require('@dollie/origins'); const { Volume } = require('memfs'); const { loadTemplate } = require('./loader'); -function Generator(name, config = {}) { +function Generator(name, config: DollieConfig = {}) { this.templateName = ''; this.templateOrigin = ''; @@ -66,6 +68,15 @@ function Generator(name, config = {}) { return duration; }; + + this.getScaffoldConfig = async function() { + const { getScaffoldConfig } = config; + if (!_.isFunction(getScaffoldConfig)) { + return {}; + } + const dollieConfigFileContent = virtualVolume.readFileSync(); + return await getScaffoldConfig(); + }; } module.exports = Generator; diff --git a/packages/@dollie/core/src/index.ts b/packages/@dollie/core/src/index.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/packages/@dollie/core/src/index.ts @@ -0,0 +1 @@ + diff --git a/packages/@dollie/core/src/interfaces.ts b/packages/@dollie/core/src/interfaces.ts new file mode 100644 index 0000000..d0752d6 --- /dev/null +++ b/packages/@dollie/core/src/interfaces.ts @@ -0,0 +1,36 @@ +import { + DollieOrigin, +} from '@dollie/origins'; +import { Change } from 'diff'; +import { Volume } from 'memfs'; +import fs from 'fs'; +import { Options as GotOptions } from 'got/dist/source'; + +export interface DiffChange extends Change { + conflicted?: boolean; + conflictGroup?: 'former' | 'current'; + lineNumber: number; +} + +export interface LoaderOptions { + httpProxyUrl?: string; + httpProxyAuth?: string; + maximumRetryCount?: number; +} + +export type LoaderConfig = LoaderOptions & GotOptions; + +export interface DollieConfig { + origins?: DollieOrigin[]; + loader?: LoaderConfig; + getScaffoldConfig?: () => void; +} + +export interface PatchTableItem { + changes: Array; + modifyLength: number; +} +export type PatchTable = Record; + +export type MemFS = typeof Volume.prototype; +export type FileSystem = MemFS | typeof fs; diff --git a/packages/@dollie/core/lib/loader.js b/packages/@dollie/core/src/loader.ts similarity index 66% rename from packages/@dollie/core/lib/loader.js rename to packages/@dollie/core/src/loader.ts index 922b116..a23a5f2 100644 --- a/packages/@dollie/core/lib/loader.js +++ b/packages/@dollie/core/src/loader.ts @@ -1,24 +1,29 @@ -const got = require('got'); -const _ = require('lodash'); -const path = require('path'); -const decompress = require('decompress'); -const { HTTPNotFoundError, HTTPTimeoutError } = require('./errors'); -const Url = require('url'); -const tunnel = require('tunnel'); -const fs = require('fs'); -const { +import got, { Options as GotOptions, RequestError } from 'got'; +import _ from 'lodash'; +import path from 'path'; +import decompress from 'decompress'; +import { + DollieError, + HTTPNotFoundError, + HTTPTimeoutError, +} from './errors'; +import Url from 'url'; +import tunnel from 'tunnel'; +import fs from 'fs'; +import { VIRTUAL_VOLUME_DESTINATION_PATHNAME, -} = require('./constants'); +} from './constants'; +import { FileSystem, LoaderConfig } from './interfaces'; -const downloadCompressedFile = async function( - url, - fileSystem, - options = {}, -) { +const downloadCompressedFile = async ( + url: string, + fileSystem: FileSystem, + options: GotOptions = {}, +) => { const startTimestamp = Date.now(); return new Promise((resolve, reject) => { - fileSystem.mkdirpSync(VIRTUAL_VOLUME_DESTINATION_PATHNAME); + fileSystem.mkdirSync(VIRTUAL_VOLUME_DESTINATION_PATHNAME, { recursive: true }); const getAbsolutePath = (filePath) => { const relativePathname = filePath.split('/').slice(1).join('/'); @@ -29,20 +34,20 @@ const downloadCompressedFile = async function( const downloader = got.stream( url, - downloaderOptions, + downloaderOptions as GotOptions & { isStream: true }, ); const fileBufferChunks = []; - downloader.on('error', (error) => { + downloader.on('error', (error: RequestError) => { const errorMessage = error.toString(); if (errorMessage.indexOf('404') !== -1) { reject(new HTTPNotFoundError()); } if (error.code === 'ETIMEDOUT') { - reject(new HTTPTimeoutError(downloaderOptions.timeout)); + reject(new HTTPTimeoutError()); } - const otherError = new Error(errorMessage); + const otherError = new DollieError(errorMessage); otherError.code = error.code || 'E_UNKNOWN'; reject(new Error(errorMessage)); }); @@ -58,7 +63,7 @@ const downloadCompressedFile = async function( for (const file of files) { const { type, path: filePath, data } = file; if (type === 'directory') { - fileSystem.mkdirpSync(getAbsolutePath(filePath)); + fileSystem.mkdirSync(getAbsolutePath(filePath), { recursive: true }); } else if (type === 'file') { fileSystem.writeFileSync(getAbsolutePath(filePath), data, { encoding: 'utf8' }); } @@ -72,15 +77,15 @@ const downloadCompressedFile = async function( }; const loadTemplate = async ( - url, - fileSystem = fs, - options = {}, + url: string, + fileSystem: FileSystem = fs, + options: LoaderConfig = {}, ) => { const traverse = async function( - url, - fileSystem = fs, + url: string, + fileSystem: FileSystem = fs, retries = 0, - options = {}, + options: LoaderConfig = {}, ) { const { httpProxyUrl = '', @@ -88,10 +93,10 @@ const loadTemplate = async ( maximumRetryCount = 3, ...originalOptions } = options; - const gotOptions = _.clone(originalOptions); + const gotOptions = _.clone(originalOptions) as GotOptions; if (httpProxyUrl) { const { hostname: host, port } = Url.parse(httpProxyUrl); - const proxy = { + const proxy: tunnel.ProxyOptions = { host, port: parseInt(port, 10), }; @@ -127,7 +132,12 @@ const loadTemplate = async ( return await traverse(url, fileSystem, 0, options); }; -module.exports = { +const readTemplateContent = (fileSystem = fs, pathname = '/template') => { + // const traverse = async function(fileSystem ) {}; +}; + +export { downloadCompressedFile, loadTemplate, + readTemplateContent, }; diff --git a/packages/@dollie/core/tsconfig.json b/packages/@dollie/core/tsconfig.json new file mode 100644 index 0000000..8d5aacc --- /dev/null +++ b/packages/@dollie/core/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } +} diff --git a/packages/@dollie/core/typings/index.d.ts b/packages/@dollie/core/typings/index.d.ts deleted file mode 100644 index 14f9718..0000000 --- a/packages/@dollie/core/typings/index.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Change } from 'diff'; - -declare type Pathname = string; -declare type TemplateFileType = 'text' | 'binary'; -declare type TemplateFileContentMap = Record; - -declare interface TextFileChange extends Change { - conflicted?: boolean; - conflictType?: 'former' | 'current'; - lineNumber: number; -} - -declare interface TemplateFileContentMapItem { - type: TemplateFileType; - contentStack?: Array>; - binaryFileBuffer?: Buffer; -} - -declare abstract class Context { - constructor(parameters) {} - - private templateFileContentMap: TemplateFileContentMap; -} - -export default { - Context, -} diff --git a/packages/@dollie/origins/lib/create.js b/packages/@dollie/origins/lib/create.js index 0ca89d8..4bef612 100644 --- a/packages/@dollie/origins/lib/create.js +++ b/packages/@dollie/origins/lib/create.js @@ -1,24 +1,26 @@ -const _ = require('lodash'); - -function createTemplateOrigin(name, metadata) { - if (!_.isString(name)) { - return null; - } - - return { - name, - handler: async function(templateName, config) { - const { getTemplateUrl, getHeaders, configPaths } = metadata; - if (!_.isFunction(getTemplateUrl)) { +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const lodash_1 = __importDefault(require("lodash")); +const createTemplateOrigin = (name, metadata) => { + if (!lodash_1.default.isString(name)) { return null; - } - const originConfig = _.pick(_.get(config, name) || {}, configPaths || []); - return { - url: await getTemplateUrl(templateName, originConfig), - headers: _.isFunction(getHeaders) ? await getHeaders(templateName, originConfig) : {}, - }; - }, - }; + } + return { + name, + handler: async (templateName, config) => { + const { getTemplateUrl, getHeaders, configPaths } = metadata; + if (!lodash_1.default.isFunction(getTemplateUrl)) { + return null; + } + const originConfig = lodash_1.default.pick(lodash_1.default.get(config, name) || {}, configPaths || []); + return { + url: await getTemplateUrl(templateName, originConfig), + headers: lodash_1.default.isFunction(getHeaders) ? await getHeaders(templateName, originConfig) : {}, + }; + }, + }; }; - -module.exports = createTemplateOrigin; +exports.default = createTemplateOrigin; diff --git a/packages/@dollie/origins/lib/index.js b/packages/@dollie/origins/lib/index.js index 423247b..f53873b 100644 --- a/packages/@dollie/origins/lib/index.js +++ b/packages/@dollie/origins/lib/index.js @@ -1,9 +1,12 @@ -const createTemplateOrigin = require('./create'); -const githubOrigin = require('./origins/github'); -const gitlabOrigin = require('./origins/gitlab'); - -module.exports = { - createTemplateOrigin, - githubOrigin, - gitlabOrigin, +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.gitlabOrigin = exports.githubOrigin = exports.createTemplateOrigin = void 0; +const create_1 = __importDefault(require("./create")); +exports.createTemplateOrigin = create_1.default; +const github_1 = __importDefault(require("./origins/github")); +exports.githubOrigin = github_1.default; +const gitlab_1 = __importDefault(require("./origins/gitlab")); +exports.gitlabOrigin = gitlab_1.default; diff --git a/packages/@dollie/origins/lib/origins/github.js b/packages/@dollie/origins/lib/origins/github.js index b479f72..4bb9d30 100644 --- a/packages/@dollie/origins/lib/origins/github.js +++ b/packages/@dollie/origins/lib/origins/github.js @@ -1,15 +1,17 @@ -const _ = require('lodash'); -const createTemplateOrigin = require('../create'); - -module.exports = createTemplateOrigin('github', { - configPaths: ['token'], - getTemplateUrl: async function(name) { - if (!_.isString(name)) { - return ''; - } - - const [repository, checkout = ''] = name.split('@'); - - return `https://api.github.com/repos/${repository}/zipball${checkout ? `/${checkout}` : ''}`; - }, +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const lodash_1 = __importDefault(require("lodash")); +const create_1 = __importDefault(require("../create")); +exports.default = create_1.default('github', { + configPaths: ['token'], + getTemplateUrl: async function (name) { + if (!lodash_1.default.isString(name)) { + return ''; + } + const [repository, checkout = ''] = name.split('@'); + return `https://api.github.com/repos/${repository}/zipball${checkout ? `/${checkout}` : ''}`; + }, }); diff --git a/packages/@dollie/origins/lib/origins/gitlab.js b/packages/@dollie/origins/lib/origins/gitlab.js index 8e083de..9bc5cb1 100644 --- a/packages/@dollie/origins/lib/origins/gitlab.js +++ b/packages/@dollie/origins/lib/origins/gitlab.js @@ -1,41 +1,34 @@ -const _ = require('lodash'); -const got = require('got'); - -const createTemplateOrigin = require('../create'); - -module.exports = createTemplateOrigin('gitlab', { - configPaths: ['token', 'host', 'port', 'protocol'], - getTemplateUrl: async function(name, config) { - if (!_.isString(name)) { - return ''; - } - - const { - protocol = 'https', - host = 'gitlab.com', - token = '', - } = config; - - const [repository, checkout = ''] = name.split('@'); - const [repositoryOwner] = name.split('/'); - - const res = await got(`${protocol}://${host}/api/v4/users/${repositoryOwner}/projects`, { - timeout: 10000, - retry: 3, - headers: token ? { token } : {}, - }); - - const projects = (JSON.parse(res.body || '[]') || []); - const targetProject = projects.filter((project) => project.path_with_namespace === repository)[0]; - - if (!targetProject) { return ''; } - - return `https://gitlab.com/api/v4/projects/${targetProject.id}/repository/archive.zip${checkout ? `?sha=${checkout}` : ''}`; - }, - getHeaders: async function(name, config) { - const { - token = '', - } = config; - return { token }; - }, +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const lodash_1 = __importDefault(require("lodash")); +const got_1 = __importDefault(require("got")); +const create_1 = __importDefault(require("../create")); +exports.default = create_1.default('gitlab', { + configPaths: ['token', 'host', 'port', 'protocol'], + getTemplateUrl: async (name, config) => { + if (!lodash_1.default.isString(name)) { + return ''; + } + const { protocol = 'https', host = 'gitlab.com', token = '', } = config; + const [repository, checkout = ''] = name.split('@'); + const [repositoryOwner] = name.split('/'); + const res = await got_1.default(`${protocol}://${host}/api/v4/users/${repositoryOwner}/projects`, { + timeout: 10000, + retry: 3, + headers: token ? { token } : {}, + }); + const projects = (JSON.parse(res.body || '[]') || []); + const targetProject = projects.filter((project) => project.path_with_namespace === repository)[0]; + if (!targetProject) { + return ''; + } + return `https://gitlab.com/api/v4/projects/${targetProject.id}/repository/archive.zip${checkout ? `?sha=${checkout}` : ''}`; + }, + getHeaders: async (name, config) => { + const { token = '', } = config; + return { token }; + }, }); diff --git a/packages/@dollie/origins/package.json b/packages/@dollie/origins/package.json index dd20fc4..04f35e9 100644 --- a/packages/@dollie/origins/package.json +++ b/packages/@dollie/origins/package.json @@ -11,6 +11,7 @@ "url": "git+https://github.com/lenconda/dollie.git" }, "scripts": { + "build": "tsc", "test": "echo \"Error: run tests from root\" && exit 1" }, "bugs": { @@ -19,5 +20,11 @@ "dependencies": { "got": "^11.8.2", "lodash": "^4.17.21" + }, + "typings": "lib/index.d.ts", + "devDependencies": { + "@types/lodash": "^4.14.170", + "@types/node": "^15.12.2", + "typescript": "^4.3.2" } } diff --git a/packages/@dollie/origins/src/create.ts b/packages/@dollie/origins/src/create.ts new file mode 100644 index 0000000..16add9d --- /dev/null +++ b/packages/@dollie/origins/src/create.ts @@ -0,0 +1,25 @@ +import _ from 'lodash'; +import { DollieOrigin, DollieOriginMetadata } from './interfaces'; + +const createTemplateOrigin = (name: string, metadata: DollieOriginMetadata): DollieOrigin => { + if (!_.isString(name)) { + return null; + } + + return { + name, + handler: async (templateName, config) => { + const { getTemplateUrl, getHeaders, configPaths } = metadata; + if (!_.isFunction(getTemplateUrl)) { + return null; + } + const originConfig = _.pick(_.get(config, name) || {}, configPaths || []); + return { + url: await getTemplateUrl(templateName, originConfig), + headers: _.isFunction(getHeaders) ? await getHeaders(templateName, originConfig) : {}, + }; + }, + }; +}; + +export default createTemplateOrigin; diff --git a/packages/@dollie/origins/src/index.ts b/packages/@dollie/origins/src/index.ts new file mode 100644 index 0000000..04eaba9 --- /dev/null +++ b/packages/@dollie/origins/src/index.ts @@ -0,0 +1,16 @@ +import createTemplateOrigin from './create'; +import githubOrigin from './origins/github'; +import gitlabOrigin from './origins/gitlab'; + +export { + createTemplateOrigin, + githubOrigin, + gitlabOrigin, +}; +export { + DollieOrigin, + DollieOriginConfig, + DollieOriginHeaders, + DollieOriginInfo, + DollieOriginMetadata, +} from './interfaces'; diff --git a/packages/@dollie/origins/src/interfaces.ts b/packages/@dollie/origins/src/interfaces.ts new file mode 100644 index 0000000..2580a93 --- /dev/null +++ b/packages/@dollie/origins/src/interfaces.ts @@ -0,0 +1,18 @@ +export type DollieOriginConfig = Record; +export type DollieOriginHeaders = Record; + +export interface DollieOriginMetadata { + configPaths: string[]; + getTemplateUrl: (name: string, config: DollieOriginConfig) => Promise; + getHeaders?: (name: string, config: DollieOriginConfig) => Promise; +} + +export interface DollieOriginInfo { + url: string; + headers?: DollieOriginHeaders; +} + +export interface DollieOrigin { + name: string; + handler: (templateName: string, config: DollieOriginConfig) => Promise; +} diff --git a/packages/@dollie/origins/src/origins/github.ts b/packages/@dollie/origins/src/origins/github.ts new file mode 100644 index 0000000..978db40 --- /dev/null +++ b/packages/@dollie/origins/src/origins/github.ts @@ -0,0 +1,15 @@ +import _ from 'lodash'; +import createTemplateOrigin from '../create'; + +export default createTemplateOrigin('github', { + configPaths: ['token'], + getTemplateUrl: async function(name) { + if (!_.isString(name)) { + return ''; + } + + const [repository, checkout = ''] = name.split('@'); + + return `https://api.github.com/repos/${repository}/zipball${checkout ? `/${checkout}` : ''}`; + }, +}); diff --git a/packages/@dollie/origins/src/origins/gitlab.ts b/packages/@dollie/origins/src/origins/gitlab.ts new file mode 100644 index 0000000..9e46547 --- /dev/null +++ b/packages/@dollie/origins/src/origins/gitlab.ts @@ -0,0 +1,40 @@ +import _ from 'lodash'; +import got from 'got'; +import createTemplateOrigin from '../create'; + +export default createTemplateOrigin('gitlab', { + configPaths: ['token', 'host', 'port', 'protocol'], + getTemplateUrl: async (name, config) => { + if (!_.isString(name)) { + return ''; + } + + const { + protocol = 'https', + host = 'gitlab.com', + token = '', + } = config; + + const [repository, checkout = ''] = name.split('@'); + const [repositoryOwner] = name.split('/'); + + const res = await got(`${protocol}://${host}/api/v4/users/${repositoryOwner}/projects`, { + timeout: 10000, + retry: 3, + headers: token ? { token } : {}, + }); + + const projects = (JSON.parse(res.body || '[]') || []); + const targetProject = projects.filter((project) => project.path_with_namespace === repository)[0]; + + if (!targetProject) { return ''; } + + return `https://gitlab.com/api/v4/projects/${targetProject.id}/repository/archive.zip${checkout ? `?sha=${checkout}` : ''}`; + }, + getHeaders: async (name, config) => { + const { + token = '', + } = config; + return { token }; + }, +}); diff --git a/packages/@dollie/origins/tsconfig.json b/packages/@dollie/origins/tsconfig.json new file mode 100644 index 0000000..8d5aacc --- /dev/null +++ b/packages/@dollie/origins/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0e59fba --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "module": "commonjs", + "removeComments": false, + "emitDecoratorMetadata": true, + "typeRoots": [ + "node_modules/@types/**/*", + "typings/**/*.d.ts" + ], + "types": ["node"], + "allowJs": false, + "esModuleInterop": true, + "experimentalDecorators": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "declaration": true, + "resolveJsonModule": true, + "target": "es2017", + "incremental": true, + "lib": [ + "es5", + "dom", + "esnext", + "es2017", + "es2015", + "es2016" + ] + }, + "exclude": [ + "node_modules/**/*", + "examples/**/*", + "scripts/**/*", + "packages/**/__tests__/**/*", + "packages/**/src/**/*.js", + ".umirc.ts" + ] +}