diff --git a/.eslintrc.js b/.eslintrc.js index 0e1140e..9e93dd0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,55 +1,72 @@ module.exports = { - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'alloy', - 'alloy/typescript', + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'alloy', + 'alloy/typescript', + ], + root: true, + env: { + node: true, + jest: true, + }, + rules: { + semi: 'off', + 'comma-dangle': ['error', 'always-multiline'], + 'switch-colon-spacing': ['error', { + 'after': true, + 'before': false, + }], + quotes: ['error', 'single'], + indent: ['error', 2, { + SwitchCase: 1, + }], + 'eol-last': ['error', 'always'], + 'space-infix-ops': 'off', + 'max-nested-callbacks': 'off', + 'max-params': 'off', + 'prefer-regex-literals': 'off', + 'no-useless-call': 'off', + 'complexity': 'off', + 'no-new-func': 'off', + '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, + }, + }, + }, ], - root: true, - env: { - node: true, - jest: true, - }, - rules: { - semi: 'off', - 'comma-dangle': ['error', 'always-multiline'], - 'switch-colon-spacing': ['error', { 'after': true, 'before': false }], - quotes: ['error', 'single'], - indent: ['error', 4, { SwitchCase: 1 }], - 'eol-last': ['error', 'always'], - 'space-infix-ops': 'off', - 'max-nested-callbacks': 'off', - 'max-params': 'off', - 'prefer-regex-literals': 'off', - 'no-useless-call': 'off', - 'complexity': 'off', - 'no-new-func': 'off', - '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/.umirc.ts b/.umirc.ts index 4d89fbe..42c351d 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -1,80 +1,82 @@ -import { defineConfig } from 'dumi'; +import { + defineConfig, +} from 'dumi'; export default defineConfig({ - title: 'Dollie.js', - outputPath: 'docs-dist', - mode: 'site', - publicPath: '/', - hash: true, - locales: [ - ['en-US', 'English'], - ['zh-CN', '中文'], - ], - copy: [ - { - from: 'docs/public', - to: 'public' - }, - ], - theme: { - '@c-primary': '#844d28', + title: 'Dollie.js', + outputPath: 'docs-dist', + mode: 'site', + publicPath: '/', + hash: true, + locales: [ + ['en-US', 'English'], + ['zh-CN', '中文'], + ], + copy: [ + { + from: 'docs/public', + to: 'public', }, - styles: [ - 'https://unpkg.zhimg.com/antd@4.16.6/dist/antd.min.css', - 'img { max-width: 86% !important; display: block; margin: 0 auto; }', - '.__dumi-default-locale-select, .__dumi-default-search-input { border-radius: 2px !important; }', - '.__dumi-default-locale-select, .__dumi-default-search-input:focus { border: 1px solid rgba(255, 255, 255, .4) !important; }', - '.__dumi-default-search-input { border: 1px solid transparent !important; }', - '.__dumi-default-menu-doc-locale { display: none !important; }', - '.__dumi-default-navbar { background-color: #0b0f13 !important; box-shadow: 0 0 0.2rem rgb(0 0 0 / 10%), 0 0.2rem 0.4rem rgb(0 0 0 / 20%) !important; }', - '.__dumi-default-search-input { background-color: rgba(255, 255, 255, .1) !important; color: rgba(255, 255, 255, .8) !important; }', - '.__dumi-default-navbar-logo { font-size: 22px !important; color: white !important; }', - '.__dumi-default-navbar nav > span > a:not(.active) { color: rgba(255, 255, 255, .6) !important; }', - '.__dumi-default-navbar nav > span > a:not(.active):hover { color: rgba(255, 255, 255, .8) !important; }', - '.__dumi-default-navbar nav > span > a.active::after { display: none !important; }', - '.__dumi-default-locale-select:hover { background-color: transparent !important; }', - '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list > li > a::after { display: none !important; }', - '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list > li > a.active { background: rgb(132, 77, 40, 0.3) !important; }', - '.__dumi-default-menu-inner ul li a::before, .__dumi-default-menu-inner ul li > span::before { display: none !important; }', - '.__dumi-default-layout-hero button { border-radius: 4px !important; }', - '.__dumi-default-menu[data-mode=\'site\'] { background: white !important; }', - 'code, pre { font-family: Menlo, Consolas, Courier, monospace !important; font-size: 14px; background: transparent !important; }', - '[data-prefers-color=dark] .markdown a { color: #844d28 !important; }', - 'pre { color: #333 !important; }', - 'h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { font-size: inherit; }', - 'https://cdn.jsdelivr.net/npm/prism-themes@1.5.0/themes/prism-vs.css', - '[data-prefers-color=dark] .__dumi-default-menu-inner ul li a:hover, [data-prefers-color=dark] .__dumi-default-menu-inner ul li > span:hover, [data-prefers-color=dark] .__dumi-default-menu-inner ul li a.active, [data-prefers-color=dark] .__dumi-default-menu-inner ul li > span.active { color: #844d28 !important; }', - '[data-prefers-color=dark] code[class*="language-"], [data-prefers-color=dark] pre[class*="language-"] { color: white !important; }', - '[data-prefers-color=dark] .markdown *:not(pre) code { color: #844d28 !important; }', - '.__dumi-default-menu[data-mode=\'site\'] { padding-top: 0 !important; }', - '[data-prefers-color=dark] .__dumi-default-navbar nav > span > a:hover, [data-prefers-color=dark] .__dumi-default-navbar nav > span > a.active, [data-prefers-color=dark] .__dumi-default-layout-footer-meta, [data-prefers-color=dark] .__dumi-default-layout-footer-meta > span:last-child::before { color: #844d28 !important; }', - '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list { margin-top: 50px; }', - '@media (min-width: 960px) { div[data-show-sidemenu="true"] .__dumi-default-layout-content { padding-left: 10%; padding-right: 10%; } }', - '@media (min-width: 1680px) { div[data-show-sidemenu="true"] .__dumi-default-layout-content { padding-left: 26%; padding-right: 26%; } }', - '.markdown blockquote { border-left-color: #844d28 !important; }', - '.markdown *:not(pre) code { color: #844d28 !important; }' + ], + theme: { + '@c-primary': '#844d28', + }, + styles: [ + 'https://unpkg.zhimg.com/antd@4.16.6/dist/antd.min.css', + 'img { max-width: 86% !important; display: block; margin: 0 auto; }', + '.__dumi-default-locale-select, .__dumi-default-search-input { border-radius: 2px !important; }', + '.__dumi-default-locale-select, .__dumi-default-search-input:focus { border: 1px solid rgba(255, 255, 255, .4) !important; }', + '.__dumi-default-search-input { border: 1px solid transparent !important; }', + '.__dumi-default-menu-doc-locale { display: none !important; }', + '.__dumi-default-navbar { background-color: #0b0f13 !important; box-shadow: 0 0 0.2rem rgb(0 0 0 / 10%), 0 0.2rem 0.4rem rgb(0 0 0 / 20%) !important; }', + '.__dumi-default-search-input { background-color: rgba(255, 255, 255, .1) !important; color: rgba(255, 255, 255, .8) !important; }', + '.__dumi-default-navbar-logo { font-size: 22px !important; color: white !important; }', + '.__dumi-default-navbar nav > span > a:not(.active) { color: rgba(255, 255, 255, .6) !important; }', + '.__dumi-default-navbar nav > span > a:not(.active):hover { color: rgba(255, 255, 255, .8) !important; }', + '.__dumi-default-navbar nav > span > a.active::after { display: none !important; }', + '.__dumi-default-locale-select:hover { background-color: transparent !important; }', + '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list > li > a::after { display: none !important; }', + '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list > li > a.active { background: rgb(132, 77, 40, 0.3) !important; }', + '.__dumi-default-menu-inner ul li a::before, .__dumi-default-menu-inner ul li > span::before { display: none !important; }', + '.__dumi-default-layout-hero button { border-radius: 4px !important; }', + '.__dumi-default-menu[data-mode=\'site\'] { background: white !important; }', + 'code, pre { font-family: Menlo, Consolas, Courier, monospace !important; font-size: 14px; background: transparent !important; }', + '[data-prefers-color=dark] .markdown a { color: #844d28 !important; }', + 'pre { color: #333 !important; }', + 'h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { font-size: inherit; }', + 'https://cdn.jsdelivr.net/npm/prism-themes@1.5.0/themes/prism-vs.css', + '[data-prefers-color=dark] .__dumi-default-menu-inner ul li a:hover, [data-prefers-color=dark] .__dumi-default-menu-inner ul li > span:hover, [data-prefers-color=dark] .__dumi-default-menu-inner ul li a.active, [data-prefers-color=dark] .__dumi-default-menu-inner ul li > span.active { color: #844d28 !important; }', + '[data-prefers-color=dark] code[class*="language-"], [data-prefers-color=dark] pre[class*="language-"] { color: white !important; }', + '[data-prefers-color=dark] .markdown *:not(pre) code { color: #844d28 !important; }', + '.__dumi-default-menu[data-mode=\'site\'] { padding-top: 0 !important; }', + '[data-prefers-color=dark] .__dumi-default-navbar nav > span > a:hover, [data-prefers-color=dark] .__dumi-default-navbar nav > span > a.active, [data-prefers-color=dark] .__dumi-default-layout-footer-meta, [data-prefers-color=dark] .__dumi-default-layout-footer-meta > span:last-child::before { color: #844d28 !important; }', + '.__dumi-default-menu[data-mode=\'site\'] .__dumi-default-menu-list { margin-top: 50px; }', + '@media (min-width: 960px) { div[data-show-sidemenu="true"] .__dumi-default-layout-content { padding-left: 10%; padding-right: 10%; } }', + '@media (min-width: 1680px) { div[data-show-sidemenu="true"] .__dumi-default-layout-content { padding-left: 26%; padding-right: 26%; } }', + '.markdown blockquote { border-left-color: #844d28 !important; }', + '.markdown *:not(pre) code { color: #844d28 !important; }' + ], + favicon: '/public/images/favicon.ico', + resolve: { + includes: ['docs'], + previewLangs: [], + }, + navs: { + 'en-US': [ + null, + { + title: 'GitHub', + path: 'https://github.com/dolliejs/dollie', + }, ], - favicon: '/public/images/favicon.ico', - resolve: { - includes: ['docs'], - previewLangs: [], - }, - navs: { - 'en-US': [ - null, - { - title: 'GitHub', - path: 'https://github.com/dolliejs/dollie', - }, - ], - 'zh-CN': [ - null, - { - title: 'GitHub', - path: 'https://github.com/dolliejs/dollie', - }, - ], - }, - logo: '/public/images/dollie.svg', - exportStatic: {}, + 'zh-CN': [ + null, + { + title: 'GitHub', + path: 'https://github.com/dolliejs/dollie', + }, + ], + }, + logo: '/public/images/dollie.svg', + exportStatic: {}, }); diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 077b094..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "editor.tabSize": 4 -} diff --git a/lerna.json b/lerna.json index 91556ff..72d8547 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { - "packages": [ - "packages/@dollie/*" - ], - "version": "3.0.1" + "packages": [ + "packages/@dollie/*" + ], + "version": "3.0.1" } diff --git a/package.json b/package.json index 78f98c8..b83e472 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,32 @@ { - "private": true, - "scripts": { - "watch:cli": "cd packages/@dollie/cli && npm run build:watch", - "watch:core": "cd packages/@dollie/core && npm run build:watch", - "watch:origins": "cd packages/@dollie/origins && npm run build:watch", - "watch": "concurrently \"npm:watch:*\"", - "docs:start": "dumi dev", - "docs:build": "dumi build", - "docs:deploy": "node scripts/docs.js", - "checksum": "node scripts/check.js", - "publish": "node scripts/publish.js" - }, - "devDependencies": { - "@types/jest": "^26.0.23", - "@types/node": "^15.12.2", - "@typescript-eslint/eslint-plugin": "^4.27.0", - "@typescript-eslint/parser": "^4.27.0", - "aliyun-oss-deploy": "^0.1.5", - "antd": "^4.16.6", - "babel-eslint": "^10.1.0", - "concurrently": "^6.2.0", - "dumi": "^1.1.22", - "eslint": "^7.28.0", - "eslint-config-alloy": "^4.1.0", - "jest": "^27.0.4", - "lerna": "^4.0.0", - "md5-file": "^5.0.0", - "typescript": "^3.7.7" - } + "private": true, + "scripts": { + "watch:cli": "cd packages/@dollie/cli && npm run build:watch", + "watch:core": "cd packages/@dollie/core && npm run build:watch", + "watch:origins": "cd packages/@dollie/origins && npm run build:watch", + "watch": "concurrently \"npm:watch:*\"", + "docs:start": "dumi dev", + "docs:build": "dumi build", + "docs:deploy": "node scripts/docs.js", + "checksum": "node scripts/check.js", + "publish": "node scripts/publish.js" + }, + "devDependencies": { + "@types/jest": "^26.0.23", + "@types/node": "^15.12.2", + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", + "aliyun-oss-deploy": "^0.1.5", + "antd": "^4.16.6", + "babel-eslint": "^10.1.0", + "concurrently": "^6.2.0", + "dumi": "^1.1.22", + "eslint": "^7.28.0", + "eslint-config-alloy": "^4.1.0", + "jest": "^27.0.4", + "lerna": "^4.0.0", + "md5-file": "^5.0.0", + "rimraf": "^3.0.2", + "typescript": "^3.7.7" + } } diff --git a/packages/@dollie/cli/package.json b/packages/@dollie/cli/package.json index e8dc26d..60b545d 100644 --- a/packages/@dollie/cli/package.json +++ b/packages/@dollie/cli/package.json @@ -1,51 +1,51 @@ { - "name": "@dollie/cli", - "version": "3.0.1", - "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" - }, - "files": [ - "bin", - "lib", - "package.json", - "README.md", - "LICENSE" - ], - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "rimraf ./lib && tsc", - "build:watch": "tsc --watch", - "test": "echo \"Error: run tests from root\" && exit 1" - }, - "bugs": { - "url": "https://github.com/lenconda/dollie/issues" - }, - "dependencies": { - "@dollie/core": "^3.0.1", - "@dollie/origins": "^3.0.1", - "chalk": "^4.1.1", - "commander": "^7.2.0", - "figlet": "^1.5.0", - "fs-extra": "^10.0.0", - "inquirer": "^8.1.1", - "lodash": "^4.17.21" - }, - "devDependencies": { - "@types/figlet": "^1.5.1", - "@types/fs-extra": "^9.0.11", - "@types/inquirer": "^7.3.2", - "@types/lodash": "^4.14.170", - "@types/node": "^15.12.2", - "rimraf": "^3.0.2" - } + "name": "@dollie/cli", + "version": "3.0.1", + "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" + }, + "files": [ + "bin", + "lib", + "package.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "rimraf ./lib && tsc", + "build:watch": "tsc --watch", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/lenconda/dollie/issues" + }, + "dependencies": { + "@dollie/core": "^3.0.1", + "@dollie/origins": "^3.0.1", + "chalk": "^4.1.1", + "commander": "^7.2.0", + "figlet": "^1.5.0", + "fs-extra": "^10.0.0", + "inquirer": "^8.1.1", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/figlet": "^1.5.1", + "@types/fs-extra": "^9.0.11", + "@types/inquirer": "^7.3.2", + "@types/lodash": "^4.14.170", + "@types/node": "^15.12.2", + "rimraf": "^3.0.2" + } } diff --git a/packages/@dollie/cli/src/commands/config.ts b/packages/@dollie/cli/src/commands/config.ts index 2559b7b..2d89218 100644 --- a/packages/@dollie/cli/src/commands/config.ts +++ b/packages/@dollie/cli/src/commands/config.ts @@ -1,48 +1,48 @@ import commander from 'commander'; import _ from 'lodash'; import { - DollieCLIConfigSchema, - writeConfig, + DollieCLIConfigSchema, + writeConfig, } from '../utils/config'; export default (config: DollieCLIConfigSchema) => { - const command = new commander.Command('config'); + const command = new commander.Command('config'); - command.description('manage CLI configurations'); + command.description('manage CLI configurations'); - command - .command('set') - .description('set value to CLI configuration item') - .arguments('[key] [value]') - .action((key: string, value: string) => { - writeConfig(key, value); - }); + command + .command('set') + .description('set value to CLI configuration item') + .arguments('[key] [value]') + .action((key: string, value: string) => { + writeConfig(key, value); + }); - command - .command('get') - .description('get value from CLI configuration item') - .arguments('[key]') - .action((key: string) => { - console.log(_.get(config, key) || ''); - }); + command + .command('get') + .description('get value from CLI configuration item') + .arguments('[key]') + .action((key: string) => { + console.log(_.get(config, key) || ''); + }); - command - .command('delete') - .description('delete value from CLI configuration item') - .arguments('[key]') - .action((key: string) => { - const [configPath] = key.split('.').slice(-1); - const parentConfigPath = key.split('.').slice(0, -1).join('.'); - const configItem = _.get(config, key) || {}; - const newConfigItem = Object.keys(configItem).reduce((result, currentKey) => { - if (configPath !== currentKey) { - result[configPath] = configItem[configPath]; - } - return result; - }, {}); + command + .command('delete') + .description('delete value from CLI configuration item') + .arguments('[key]') + .action((key: string) => { + const [configPath] = key.split('.').slice(-1); + const parentConfigPath = key.split('.').slice(0, -1).join('.'); + const configItem = _.get(config, key) || {}; + const newConfigItem = Object.keys(configItem).reduce((result, currentKey) => { + if (configPath !== currentKey) { + result[configPath] = configItem[configPath]; + } + return result; + }, {}); - writeConfig(parentConfigPath, newConfigItem); - }); + writeConfig(parentConfigPath, newConfigItem); + }); - return command; + return command; }; diff --git a/packages/@dollie/cli/src/commands/index.ts b/packages/@dollie/cli/src/commands/index.ts index b0851a0..e113574 100644 --- a/packages/@dollie/cli/src/commands/index.ts +++ b/packages/@dollie/cli/src/commands/index.ts @@ -3,7 +3,7 @@ import init from './init'; import origin from './origin'; export default { - config, - init, - origin, + config, + init, + origin, }; diff --git a/packages/@dollie/cli/src/commands/init.ts b/packages/@dollie/cli/src/commands/init.ts index 0462534..0747610 100644 --- a/packages/@dollie/cli/src/commands/init.ts +++ b/packages/@dollie/cli/src/commands/init.ts @@ -1,270 +1,289 @@ import commander from 'commander'; import { - DollieCLIConfigSchema, + DollieCLIConfigSchema, } from '../utils/config'; import { - Context, - ConflictSolverData, - ConflictSolveResult, - parseFileTextToMergeBlocks, + Context, + ConflictSolverData, + ConflictSolveResult, + parseFileTextToMergeBlocks, } from '@dollie/core'; -import { writeGeneratedFiles } from '../utils/writer'; import { - ErrorLogger, - InfoLogger, + writeGeneratedFiles, +} from '../utils/writer'; +import { + ErrorLogger, + InfoLogger, } from '../logger'; import _ from 'lodash'; import inquirer from 'inquirer'; import chalk from 'chalk'; import figlet from 'figlet'; -import { loadOrigins } from '../../../origins/lib'; +import { + loadOrigins, +} from '../../../origins/lib'; export type ConflictSolveApproachType = 'simple' | 'select' | 'edit' | 'ignore'; export type ManualResult = 'all' | 'none' | 'former' | 'current'; const conflictsSolver = async ( - data: ConflictSolverData, - onMessage: (message: string) => Promise | void = _.noop, + data: ConflictSolverData, + onMessage: (message: string) => Promise | void = _.noop, ): Promise => { - const { block: originalMergeBlock, total, index, pathname } = data; - const block = _.clone(originalMergeBlock); + const { + block: originalMergeBlock, + total, + index, + pathname, + } = data; + const block = _.clone(originalMergeBlock); + + const lines = _.flatten(['former', 'current'].map((type) => { + return ((_.get(block, `values.${type}`) || []) as string[]).map((line) => { + return `[${type}] ${line.slice(0, -1)}`; + }); + })); + + await onMessage( + 'Solving ' + + (index + 1) + + ' of ' + + total + + ' conflicts in `' + + pathname + + '`:\n' + + lines.join('\n'), + ); + + let approachType: ConflictSolveApproachType; + const approachTypeAnswer = await inquirer.prompt( + [ + { + name: 'type', + type: 'list', + message: 'Select an appropriate approach to solve this conflict', + choices: [ + { + value: 'simple', + name: 'Simply select which group should be kept', + }, + { + value: 'select', + name: 'Select conflicted lines to be kept manually', + }, + { + value: 'edit', + name: 'Open an editor to edit the whole file', + }, + { + value: 'ignore', + name: 'Ignore this conflict', + }, + ], + }, + ], + ); + approachType = approachTypeAnswer.type as ConflictSolveApproachType; + + if (!approachType) { + return null; + } + + if (!['simple', 'select', 'edit', 'ignore'].includes(approachType)) { + approachType = 'simple'; + } + + let conflictSolveResult: ConflictSolveResult; + + switch (approachType) { + case 'ignore': { + return 'ignored'; + } - const lines = _.flatten(['former', 'current'].map((type) => { - return ((_.get(block, `values.${type}`) || []) as string[]).map((line) => { - return `[${type}] ${line.slice(0, -1)}`; - }); - })); - - await onMessage( - 'Solving ' - + (index + 1) - + ' of ' - + total - + ' conflicts in `' - + pathname - + '`:\n' - + lines.join('\n'), - ); - - let approachType: ConflictSolveApproachType; - const approachTypeAnswer = await inquirer.prompt([ - { - name: 'type', + case 'simple': { + const {result} = (await inquirer.prompt( + [ + { + name: 'result', type: 'list', - message: 'Select an appropriate approach to solve this conflict', + message: 'Select a group of lines which you want to keep', choices: [ - { - value: 'simple', - name: 'Simply select which group should be kept', - }, - { - value: 'select', - name: 'Select conflicted lines to be kept manually', - }, - { - value: 'edit', - name: 'Open an editor to edit the whole file', - }, - { - value: 'ignore', - name: 'Ignore this conflict', - }, + { + value: 'all', + name: 'I want to keep all lines', + }, + { + value: 'none', + name: 'I want to discard all lines', + }, ], - }, - ]); - approachType = approachTypeAnswer.type as ConflictSolveApproachType; - - if (!approachType) { - return null; - } - - if (!['simple', 'select', 'edit', 'ignore'].includes(approachType)) { - approachType = 'simple'; - } - - let conflictSolveResult: ConflictSolveResult; - - switch (approachType) { - case 'ignore': { - return 'ignored'; + }, + ], + )) as { result: ManualResult }; + + switch (result) { + case 'all': { + block.values.current = Array + .from(block.values.former) + .concat(Array.from(block.values.current)); + block.values.former = []; + break; } - - case 'simple': { - const {result} = (await inquirer.prompt([ - { - name: 'result', - type: 'list', - message: 'Select a group of lines which you want to keep', - choices: [ - { - value: 'all', - name: 'I want to keep all lines', - }, - { - value: 'none', - name: 'I want to discard all lines', - }, - ], - }, - ])) as { result: ManualResult }; - - switch (result) { - case 'all': { - block.values.current = Array - .from(block.values.former) - .concat(Array.from(block.values.current)); - block.values.former = []; - break; - } - case 'none': { - block.values.former = []; - block.values.current = []; - break; - } - default: - break; - } - - block.status = 'OK'; - conflictSolveResult = block; - break; + case 'none': { + block.values.former = []; + block.values.current = []; + break; } + default: + break; + } + + block.status = 'OK'; + conflictSolveResult = block; + break; + } - case 'select': { - const { keeps } = (await inquirer.prompt([ - { - name: 'keeps', - type: 'checkbox', - message: 'Select the lines that should be kept:', - choices: lines.map((line) => ({ - name: line, - value: line, - })), - }, - ])) as { keeps: string[] }; - - block.values.former = []; - block.values.current = []; - - for (const keep of keeps) { - const formerFlag = '[former] '; - const currentFlag = '[current] '; - - let currentLine: string; - - if (keep.startsWith(formerFlag)) { - currentLine = keep.slice(formerFlag.length); - } else if (keep.startsWith(currentFlag)) { - currentLine = keep.slice(currentFlag.length); - } - - if (currentLine) { - block.values.current.push(`${currentLine}\n`); - } - } - - conflictSolveResult = block; - break; + case 'select': { + const { keeps } = (await inquirer.prompt( + [ + { + name: 'keeps', + type: 'checkbox', + message: 'Select the lines that should be kept:', + choices: lines.map((line) => ({ + name: line, + value: line, + })), + }, + ], + )) as { keeps: string[] }; + + block.values.former = []; + block.values.current = []; + + for (const keep of keeps) { + const formerFlag = '[former] '; + const currentFlag = '[current] '; + + let currentLine: string; + + if (keep.startsWith(formerFlag)) { + currentLine = keep.slice(formerFlag.length); + } else if (keep.startsWith(currentFlag)) { + currentLine = keep.slice(currentFlag.length); } - case 'edit': { - const { content } = (await inquirer.prompt([ - { - type: 'editor', - name: 'content', - message: 'Edit conflicts with editor', - default: block.values.former - .concat(block.values.current) - .map((value) => value.slice(0, -1)) - .join('\n'), - }, - ])) as { content: string }; - - [conflictSolveResult] = parseFileTextToMergeBlocks(content); - break; + if (currentLine) { + block.values.current.push(`${currentLine}\n`); } + } - default: - break; + conflictSolveResult = block; + break; } - await onMessage( - 'Here is the result of currently-solved conflict:\n' - + ( - _.isString(conflictSolveResult) - ? conflictSolveResult - : ((_.get(conflictSolveResult, 'values.current') || []) as string[]).join('') - ), - ); - - const { confirm } = await inquirer.prompt([ - { - name: 'confirm', - type: 'confirm', - message: 'Do you confirm this result?', - }, - ]) as { confirm: boolean }; - - if (confirm) { - return conflictSolveResult; - } else { - return null; + case 'edit': { + const { content } = (await inquirer.prompt( + [ + { + type: 'editor', + name: 'content', + message: 'Edit conflicts with editor', + default: block.values.former + .concat(block.values.current) + .map((value) => value.slice(0, -1)) + .join('\n'), + }, + ], + )) as { content: string }; + + [conflictSolveResult] = parseFileTextToMergeBlocks(content); + break; } + + default: + break; + } + + await onMessage( + 'Here is the result of currently-solved conflict:\n' + + ( + _.isString(conflictSolveResult) + ? conflictSolveResult + : ((_.get(conflictSolveResult, 'values.current') || []) as string[]).join('') + ), + ); + + const { confirm } = await inquirer.prompt([ + { + name: 'confirm', + type: 'confirm', + message: 'Do you confirm this result?', + }, + ]) as { confirm: boolean }; + + if (confirm) { + return conflictSolveResult; + } else { + return null; + } }; export default (config: DollieCLIConfigSchema) => { - const command = new commander.Command('init'); - - command - .description('init a project with an appropriate template') - .arguments('[template] [name]') - .action(async (template: string, name: string) => { - console.log(figlet.textSync('dollie.js')); - try { - const errorLogger = new ErrorLogger(); - const infoLogger = new InfoLogger(); - - infoLogger.log('Loading origins...'); - - const context = new Context(name, template, { - generator: { - origins: await loadOrigins(config.origins || {}), - origin: config.origin || {}, - loader: _.get(config, 'loader'), - getTemplateProps: async (questions) => { - const answers = await inquirer.prompt(questions); - return answers; - }, - conflictsSolver: async (data) => { - return await conflictsSolver(data, async (message) => { - infoLogger.log(message); - return Promise.resolve(); - }); - }, - }, - onMessage: (message) => infoLogger.log(message), - onError: (error) => { - errorLogger.log(error.message); - process.exit(1); - }, - }); - - const result = await context.generate(); - - if (result) { - writeGeneratedFiles(result, name); - } - - if (_.isArray(result.conflicts) && result.conflicts.length > 0) { - infoLogger.log( - `Generated template files with ${result.conflicts.length} ignored conflicted file(s):\n` - + result.conflicts.map((conflict) => { - return chalk.yellow(` - ${conflict}`); - }).join('\n'), - ); - } - } catch {} + const command = new commander.Command('init'); + + command + .description('init a project with an appropriate template') + .arguments('[template] [name]') + .action(async (template: string, name: string) => { + console.log(figlet.textSync('dollie.js')); + try { + const errorLogger = new ErrorLogger(); + const infoLogger = new InfoLogger(); + + infoLogger.log('Loading origins...'); + + const context = new Context(name, template, { + generator: { + origins: await loadOrigins(config.origins || {}), + origin: config.origin || {}, + loader: _.get(config, 'loader'), + getTemplateProps: async (questions) => { + const answers = await inquirer.prompt(questions); + return answers; + }, + conflictsSolver: async (data) => { + return await conflictsSolver(data, async (message) => { + infoLogger.log(message); + return Promise.resolve(); + }); + }, + }, + onMessage: (message) => infoLogger.log(message), + onError: (error) => { + errorLogger.log(error.message); + process.exit(1); + }, }); - return command; + const result = await context.generate(); + + if (result) { + writeGeneratedFiles(result, name); + } + + if (_.isArray(result.conflicts) && result.conflicts.length > 0) { + infoLogger.log( + 'Generated template files with ' + + result.conflicts.length + + ' ignored conflicted file(s):\n' + + result.conflicts.map((conflict) => { + return chalk.yellow(` - ${conflict}`); + }).join('\n'), + ); + } + } catch {} + }); + + return command; }; diff --git a/packages/@dollie/cli/src/commands/origin.ts b/packages/@dollie/cli/src/commands/origin.ts index 543de4b..0e84f67 100644 --- a/packages/@dollie/cli/src/commands/origin.ts +++ b/packages/@dollie/cli/src/commands/origin.ts @@ -1,39 +1,39 @@ import commander from 'commander'; import _ from 'lodash'; import { - DollieCLIConfigSchema, - writeConfig, + DollieCLIConfigSchema, + writeConfig, } from '../utils/config'; export default (config: DollieCLIConfigSchema) => { - const command = new commander.Command('origin'); + const command = new commander.Command('origin'); - command.description('manage template origins'); + command.description('manage template origins'); - command - .command('add') - .description('add a template origins') - .arguments('[name] [pathname]') - .action((name: string, pathname: string) => { - writeConfig(`origins.${name}`, pathname); - }); + command + .command('add') + .description('add a template origins') + .arguments('[name] [pathname]') + .action((name: string, pathname: string) => { + writeConfig(`origins.${name}`, pathname); + }); - command - .command('delete') - .description('delete a template origin from origins') - .arguments('[name]') - .action((name: string) => { - const origins = _.get(config, 'origins') || {}; - writeConfig( - 'origins', - Object.keys(origins).reduce((result, currentName) => { - if (name !== currentName) { - result[name] = origins[name]; - } - return result; - }, {}), - ); - }); + command + .command('delete') + .description('delete a template origin from origins') + .arguments('[name]') + .action((name: string) => { + const origins = _.get(config, 'origins') || {}; + writeConfig( + 'origins', + Object.keys(origins).reduce((result, currentName) => { + if (name !== currentName) { + result[name] = origins[name]; + } + return result; + }, {}), + ); + }); - return command; + return command; }; diff --git a/packages/@dollie/cli/src/index.ts b/packages/@dollie/cli/src/index.ts index ea187b1..73b9cee 100644 --- a/packages/@dollie/cli/src/index.ts +++ b/packages/@dollie/cli/src/index.ts @@ -1,8 +1,12 @@ import commands from './commands'; import commander from 'commander'; import _ from 'lodash'; -import { initializeConfig } from './init'; -import { readConfig } from './utils/config'; +import { + initializeConfig, +} from './init'; +import { + readConfig, +} from './utils/config'; import fs from 'fs'; import path from 'path'; @@ -10,8 +14,7 @@ initializeConfig(); const config = readConfig(); const packageJsonContent = - fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8') - || '{}'; + fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8') || '{}'; const packageJson = JSON.parse(packageJsonContent); const program = new commander.Command(); @@ -19,10 +22,10 @@ const program = new commander.Command(); program.version(packageJson.version || 'unknown'); for (const commandKey of Object.keys(commands)) { - const commandGenerator = commands[commandKey]; - if (_.isFunction(commandGenerator)) { - program.addCommand(commandGenerator(config)); - } + const commandGenerator = commands[commandKey]; + if (_.isFunction(commandGenerator)) { + program.addCommand(commandGenerator(config)); + } } program.parse(process.argv); diff --git a/packages/@dollie/cli/src/init.ts b/packages/@dollie/cli/src/init.ts index 01fa830..a674697 100644 --- a/packages/@dollie/cli/src/init.ts +++ b/packages/@dollie/cli/src/init.ts @@ -1,44 +1,44 @@ import fs from 'fs-extra'; import { - DEFAULT_CONFIG, - CONFIG_DIR, - SYSTEM_CONFIG_PATHNAME, + DEFAULT_CONFIG, + CONFIG_DIR, + SYSTEM_CONFIG_PATHNAME, } from './constants'; const initializeConfig = () => { - const writeSystemConfig = () => { - fs.writeFileSync(SYSTEM_CONFIG_PATHNAME, JSON.stringify(DEFAULT_CONFIG, null, 2)); - }; + const writeSystemConfig = () => { + fs.writeFileSync(SYSTEM_CONFIG_PATHNAME, JSON.stringify(DEFAULT_CONFIG, null, 2)); + }; - const dirExistence = fs.existsSync(CONFIG_DIR); + const dirExistence = fs.existsSync(CONFIG_DIR); - if (!dirExistence) { - fs.mkdirpSync(CONFIG_DIR); - } + if (!dirExistence) { + fs.mkdirpSync(CONFIG_DIR); + } - const dirStat = fs.statSync(CONFIG_DIR); + const dirStat = fs.statSync(CONFIG_DIR); - if (!dirStat.isDirectory()) { - try { - fs.removeSync(CONFIG_DIR); - fs.mkdirpSync(CONFIG_DIR); - } catch {} - } + if (!dirStat.isDirectory()) { + try { + fs.removeSync(CONFIG_DIR); + fs.mkdirpSync(CONFIG_DIR); + } catch {} + } - const systemConfigFileExistence = fs.existsSync(SYSTEM_CONFIG_PATHNAME); + const systemConfigFileExistence = fs.existsSync(SYSTEM_CONFIG_PATHNAME); - if (!systemConfigFileExistence) { - writeSystemConfig(); - } + if (!systemConfigFileExistence) { + writeSystemConfig(); + } - const systemConfigFileStat = fs.statSync(SYSTEM_CONFIG_PATHNAME); + const systemConfigFileStat = fs.statSync(SYSTEM_CONFIG_PATHNAME); - if (!systemConfigFileStat.isFile()) { - fs.removeSync(SYSTEM_CONFIG_PATHNAME); - writeSystemConfig(); - } + if (!systemConfigFileStat.isFile()) { + fs.removeSync(SYSTEM_CONFIG_PATHNAME); + writeSystemConfig(); + } }; export { - initializeConfig, + initializeConfig, }; diff --git a/packages/@dollie/cli/src/logger.ts b/packages/@dollie/cli/src/logger.ts index 80edd35..b2eda6f 100644 --- a/packages/@dollie/cli/src/logger.ts +++ b/packages/@dollie/cli/src/logger.ts @@ -2,48 +2,48 @@ import chalk from 'chalk'; import _ from 'lodash'; export class Logger { - public constructor( - private color: string, - private backgroundColor: string, - private level: string = 'INFO', - private logger = console, - ) {} - - public log(message: string): void { - const currentTime = new Date().toLocaleString(); - - const messageString = `${this.level ? ` - ${this.level.toUpperCase()} - ` : ''}` + message; - let chalkedMessageString: string; - - if (!this.color) { - chalkedMessageString = messageString; - } else if (!_.isFunction(chalk[this.color])) { - chalkedMessageString = messageString; - } else { - chalkedMessageString = chalk[this.color](messageString); - } - - if (this.backgroundColor && _.isFunction(chalk[this.backgroundColor])) { - chalkedMessageString = chalk[this.backgroundColor](chalkedMessageString); - } - - this.logger.log(chalk.gray(currentTime) + chalkedMessageString); + public constructor( + private color: string, + private backgroundColor: string, + private level: string = 'INFO', + private logger = console, + ) {} + + public log(message: string): void { + const currentTime = new Date().toLocaleString(); + + const messageString = `${this.level ? ` - ${this.level.toUpperCase()} - ` : ''}` + message; + let chalkedMessageString: string; + + if (!this.color) { + chalkedMessageString = messageString; + } else if (!_.isFunction(chalk[this.color])) { + chalkedMessageString = messageString; + } else { + chalkedMessageString = chalk[this.color](messageString); } + + if (this.backgroundColor && _.isFunction(chalk[this.backgroundColor])) { + chalkedMessageString = chalk[this.backgroundColor](chalkedMessageString); + } + + this.logger.log(chalk.gray(currentTime) + chalkedMessageString); + } } class InfoLogger extends Logger { - public constructor() { - super(null, null); - } + public constructor() { + super(null, null); + } } class ErrorLogger extends Logger { - public constructor() { - super('red', null, 'ERROR'); - } + public constructor() { + super('red', null, 'ERROR'); + } } export { - InfoLogger, - ErrorLogger, + InfoLogger, + ErrorLogger, }; diff --git a/packages/@dollie/cli/src/utils/config.ts b/packages/@dollie/cli/src/utils/config.ts index 4a294af..1c69a59 100644 --- a/packages/@dollie/cli/src/utils/config.ts +++ b/packages/@dollie/cli/src/utils/config.ts @@ -1,6 +1,8 @@ import fs from 'fs'; import _ from 'lodash'; -import { LoaderConfig } from '../../../core/lib/interfaces'; +import { + LoaderConfig, +} from '@dollie/core'; import { SYSTEM_CONFIG_PATHNAME } from '../constants'; interface DollieCLIConfigSchema { @@ -10,48 +12,48 @@ interface DollieCLIConfigSchema { } const readConfig = (): DollieCLIConfigSchema => { - if ( - !fs.existsSync(SYSTEM_CONFIG_PATHNAME) - || !fs.statSync(SYSTEM_CONFIG_PATHNAME).isFile() - ) { - return {}; - } - - const content = fs.readFileSync(SYSTEM_CONFIG_PATHNAME).toString(); - - try { - return JSON.parse(content); - } catch { - return {}; - } + if ( + !fs.existsSync(SYSTEM_CONFIG_PATHNAME) || + !fs.statSync(SYSTEM_CONFIG_PATHNAME).isFile() + ) { + return {}; + } + + const content = fs.readFileSync(SYSTEM_CONFIG_PATHNAME).toString(); + + try { + return JSON.parse(content); + } catch { + return {}; + } }; const writeConfig = (key: string, value: any) => { - if ( - !fs.existsSync(SYSTEM_CONFIG_PATHNAME) - || !fs.statSync(SYSTEM_CONFIG_PATHNAME).isFile() - ) { - return; - } + if ( + !fs.existsSync(SYSTEM_CONFIG_PATHNAME) || + !fs.statSync(SYSTEM_CONFIG_PATHNAME).isFile() + ) { + return; + } - const content = fs.readFileSync(SYSTEM_CONFIG_PATHNAME).toString(); + const content = fs.readFileSync(SYSTEM_CONFIG_PATHNAME).toString(); - let parsedConfig; + let parsedConfig; - try { - parsedConfig = JSON.parse(content); - } catch {} + try { + parsedConfig = JSON.parse(content); + } catch {} - if (!parsedConfig) { - return; - } + if (!parsedConfig) { + return; + } - const newConfig = _.set(parsedConfig, key, value); - fs.writeFileSync(SYSTEM_CONFIG_PATHNAME, JSON.stringify(newConfig, null, 2)); + const newConfig = _.set(parsedConfig, key, value); + fs.writeFileSync(SYSTEM_CONFIG_PATHNAME, JSON.stringify(newConfig, null, 2)); }; export { - readConfig, - writeConfig, - DollieCLIConfigSchema, + readConfig, + writeConfig, + DollieCLIConfigSchema, }; diff --git a/packages/@dollie/cli/src/utils/writer.ts b/packages/@dollie/cli/src/utils/writer.ts index 264ca91..866dbe8 100644 --- a/packages/@dollie/cli/src/utils/writer.ts +++ b/packages/@dollie/cli/src/utils/writer.ts @@ -1,37 +1,37 @@ import { - DollieGeneratorResult, + DollieGeneratorResult, } from '@dollie/core/lib/interfaces'; import fs from 'fs-extra'; import path from 'path'; const writeGeneratedFiles = (data: DollieGeneratorResult, projectName: string) => { - const { files = {} } = data; - const destinationPathname = path.resolve(process.cwd(), projectName); + const { files = {} } = data; + const destinationPathname = path.resolve(process.cwd(), projectName); - if ( - fs.existsSync(destinationPathname) - && !fs.statSync(destinationPathname).isDirectory() - ) { - fs.removeSync(destinationPathname); - } - - if (!fs.existsSync(destinationPathname)) { - fs.mkdirpSync(destinationPathname); - } + if ( + fs.existsSync(destinationPathname) && + !fs.statSync(destinationPathname).isDirectory() + ) { + fs.removeSync(destinationPathname); + } - for (const pathname of Object.keys(files)) { - const content = files[pathname]; - const dirname = pathname.split(path.sep).slice(0, -1).join(path.sep); - const absoluteDirname = path.resolve(destinationPathname, dirname); + if (!fs.existsSync(destinationPathname)) { + fs.mkdirpSync(destinationPathname); + } - if (dirname && !fs.existsSync(absoluteDirname)) { - fs.mkdirpSync(absoluteDirname); - } + for (const pathname of Object.keys(files)) { + const content = files[pathname]; + const dirname = pathname.split(path.sep).slice(0, -1).join(path.sep); + const absoluteDirname = path.resolve(destinationPathname, dirname); - fs.writeFileSync(path.resolve(destinationPathname, pathname), content); + if (dirname && !fs.existsSync(absoluteDirname)) { + fs.mkdirpSync(absoluteDirname); } + + fs.writeFileSync(path.resolve(destinationPathname, pathname), content); + } }; export { - writeGeneratedFiles, + writeGeneratedFiles, }; diff --git a/packages/@dollie/cli/tsconfig.json b/packages/@dollie/cli/tsconfig.json index a75d279..50764ca 100644 --- a/packages/@dollie/cli/tsconfig.json +++ b/packages/@dollie/cli/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "../../../tsconfig.package.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "outDir": "./lib", - "rootDir": "./src", - "tsBuildInfoFile": "./lib/.tsbuildinfo" - } + "extends": "../../../tsconfig.package.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } } diff --git a/packages/@dollie/core/package.json b/packages/@dollie/core/package.json index 9e81341..f23b6c8 100644 --- a/packages/@dollie/core/package.json +++ b/packages/@dollie/core/package.json @@ -1,57 +1,57 @@ { - "name": "@dollie/core", - "version": "3.0.1", - "description": "Core of Dollie", - "author": "lenconda ", - "homepage": "https://github.com/lenconda/dollie#readme", - "license": "MIT", - "main": "lib/index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/lenconda/dollie.git" - }, - "scripts": { - "build": "rimraf ./lib && tsc", - "build:watch": "tsc --watch", - "test": "echo \"Error: run tests from root\" && exit 1" - }, - "typings": "lib/index.d.ts", - "bugs": { - "url": "https://github.com/lenconda/dollie/issues" - }, - "files": [ - "lib", - "package.json", - "README.md", - "LICENSE" - ], - "publishConfig": { - "access": "public" - }, - "dependencies": { - "@dollie/origins": "^3.0.1", - "@types/ejs": "^3.0.6", - "decompress": "^4.2.1", - "diff": "^5.0.0", - "ejs": "^3.1.6", - "got": "^11.8.2", - "inquirer": "^8.1.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "memfs": "^3.2.2", - "minimatch": "^3.0.4", - "require-from-string": "^2.0.2", - "tunnel": "^0.0.6" - }, - "devDependencies": { - "@types/diff": "^5.0.0", - "@types/fs-extra": "^9.0.11", - "@types/inquirer": "^7.3.1", - "@types/lodash": "^4.14.170", - "@types/minimatch": "^3.0.4", - "@types/node": "^15.12.2", - "@types/require-from-string": "^1.2.0", - "fs-extra": "^10.0.0", - "rimraf": "^3.0.2" - } + "name": "@dollie/core", + "version": "3.0.1", + "description": "Core of Dollie", + "author": "lenconda ", + "homepage": "https://github.com/lenconda/dollie#readme", + "license": "MIT", + "main": "lib/index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/lenconda/dollie.git" + }, + "scripts": { + "build": "rimraf ./lib && tsc", + "build:watch": "tsc --watch", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "typings": "lib/index.d.ts", + "bugs": { + "url": "https://github.com/lenconda/dollie/issues" + }, + "files": [ + "lib", + "package.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@dollie/origins": "^3.0.1", + "@types/ejs": "^3.0.6", + "decompress": "^4.2.1", + "diff": "^5.0.0", + "ejs": "^3.1.6", + "got": "^11.8.2", + "inquirer": "^8.1.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "memfs": "^3.2.2", + "minimatch": "^3.0.4", + "require-from-string": "^2.0.2", + "tunnel": "^0.0.6" + }, + "devDependencies": { + "@types/diff": "^5.0.0", + "@types/fs-extra": "^9.0.11", + "@types/inquirer": "^7.3.1", + "@types/lodash": "^4.14.170", + "@types/minimatch": "^3.0.4", + "@types/node": "^15.12.2", + "@types/require-from-string": "^1.2.0", + "fs-extra": "^10.0.0", + "rimraf": "^3.0.2" + } } diff --git a/packages/@dollie/core/src/constants.ts b/packages/@dollie/core/src/constants.ts index 9224acd..53c5ed8 100644 --- a/packages/@dollie/core/src/constants.ts +++ b/packages/@dollie/core/src/constants.ts @@ -4,19 +4,19 @@ const EXTEND_TEMPLATE_PATHNAME_PREFIX = '/extends'; const MAIN_TEMPLATE_PATHNAME_PREFIX = '/main'; const TEMPLATE_FILE_PREFIX = '__template.'; const TEMPLATE_CONFIG_FILE_NAMES = [ - 'dollie.json', - 'dollie.js', - '.dollie.json', - '.dollie.js', + 'dollie.json', + 'dollie.js', + '.dollie.json', + '.dollie.js', ]; const EXTEND_TEMPLATE_PREFIX = '$EXTEND'; export { - TEMPLATE_CACHE_PATHNAME_PREFIX, - EXTEND_TEMPLATE_LABEL_PREFIX, - EXTEND_TEMPLATE_PATHNAME_PREFIX, - MAIN_TEMPLATE_PATHNAME_PREFIX, - TEMPLATE_FILE_PREFIX, - TEMPLATE_CONFIG_FILE_NAMES, - EXTEND_TEMPLATE_PREFIX, + TEMPLATE_CACHE_PATHNAME_PREFIX, + EXTEND_TEMPLATE_LABEL_PREFIX, + EXTEND_TEMPLATE_PATHNAME_PREFIX, + MAIN_TEMPLATE_PATHNAME_PREFIX, + TEMPLATE_FILE_PREFIX, + TEMPLATE_CONFIG_FILE_NAMES, + EXTEND_TEMPLATE_PREFIX, }; diff --git a/packages/@dollie/core/src/context.ts b/packages/@dollie/core/src/context.ts index e8079a3..77bb5ea 100644 --- a/packages/@dollie/core/src/context.ts +++ b/packages/@dollie/core/src/context.ts @@ -1,12 +1,14 @@ import _ from 'lodash'; -import { DollieError } from './errors'; +import { + DollieError, +} from './errors'; import Generator from './generator'; import { - DollieConfig, - DollieContextStatusMap, - ErrorHandler, - MessageHandler, - StatusChangeHandler, + DollieConfig, + DollieContextStatusMap, + ErrorHandler, + MessageHandler, + StatusChangeHandler, } from './interfaces'; class Context { @@ -18,115 +20,115 @@ class Context { private statusMap: DollieContextStatusMap; public constructor( - protected projectName: string, - private templateOriginName: string, - private config: DollieConfig = {}, + protected projectName: string, + private templateOriginName: string, + private config: DollieConfig = {}, ) { - const { onStatusChange, onError, onMessage } = config; - this.errorHandler = _.isFunction(onError) ? onError : _.noop; - this.messageHandler = _.isFunction(onMessage) ? onMessage : _.noop; - this.statusChangeHandler = _.isFunction(onStatusChange) ? onStatusChange : _.noop; - this.statusMap = this.initializeStatus(); + const { onStatusChange, onError, onMessage } = config; + this.errorHandler = _.isFunction(onError) ? onError : _.noop; + this.messageHandler = _.isFunction(onMessage) ? onMessage : _.noop; + this.statusChangeHandler = _.isFunction(onStatusChange) ? onStatusChange : _.noop; + this.statusMap = this.initializeStatus(); } public async generate() { - try { - for (const lifecycle of this.lifecycleList) { - const lifecycleExecutor = this[lifecycle]; - if (_.isFunction(lifecycleExecutor)) { - await lifecycleExecutor.call(this); - } - } - return this.generator.getResult(); - } catch (e) { - this.errorHandler(new DollieError(e.message || 'unknown error')); + try { + for (const lifecycle of this.lifecycleList) { + const lifecycleExecutor = this[lifecycle]; + if (_.isFunction(lifecycleExecutor)) { + await lifecycleExecutor.call(this); + } } + return this.generator.getResult(); + } catch (e) { + this.errorHandler(new DollieError(e.message || 'unknown error')); + } } protected async bootstrap() { - this.updateRunningStatus('bootstrap'); - const { - projectName, - templateOriginName, - config, - } = this; - this.generator = new Generator(projectName, templateOriginName, { - ...(config.generator || {}), - onMessage: this.messageHandler, - }); - this.generator.checkInputs(); - await this.generator.initialize(); - this.generator.checkContext(); - this.updateFinishedStatus('bootstrap'); + this.updateRunningStatus('bootstrap'); + const { + projectName, + templateOriginName, + config, + } = this; + this.generator = new Generator(projectName, templateOriginName, { + ...(config.generator || {}), + onMessage: this.messageHandler, + }); + this.generator.checkInputs(); + await this.generator.initialize(); + this.generator.checkContext(); + this.updateFinishedStatus('bootstrap'); } protected async load() { - this.updateRunningStatus('load'); - await this.generator.loadTemplate(); - await this.generator.queryAllTemplateProps(); - this.updateFinishedStatus('load'); + this.updateRunningStatus('load'); + await this.generator.loadTemplate(); + await this.generator.queryAllTemplateProps(); + this.updateFinishedStatus('load'); } protected write() { - this.updateRunningStatus('write'); - this.generator.copyTemplateFileToCacheTable(); - this.generator.deleteFiles(); - this.generator.mergeTemplateFiles(); - this.updateFinishedStatus('write'); + this.updateRunningStatus('write'); + this.generator.copyTemplateFileToCacheTable(); + this.generator.deleteFiles(); + this.generator.mergeTemplateFiles(); + this.updateFinishedStatus('write'); } protected async conflict() { - this.updateRunningStatus('conflict'); - await this.generator.resolveConflicts(); - this.updateFinishedStatus('conflict'); + this.updateRunningStatus('conflict'); + await this.generator.resolveConflicts(); + this.updateFinishedStatus('conflict'); } protected async end() { - this.updateRunningStatus('end'); - await this.generator.runCleanups(); - this.updateFinishedStatus('end'); + this.updateRunningStatus('end'); + await this.generator.runCleanups(); + this.updateFinishedStatus('end'); } private initializeStatus() { - return this.lifecycleList.reduce((result, lifecycleName) => { - result[lifecycleName] = 'pending'; - return result; - }, {} as DollieContextStatusMap); + return this.lifecycleList.reduce((result, lifecycleName) => { + result[lifecycleName] = 'pending'; + return result; + }, {} as DollieContextStatusMap); } private updateRunningStatus(lifecycleName: string) { - const lifecycleIndex = Object.keys(this.statusMap).findIndex((currentLifecycleName) => { - return currentLifecycleName === lifecycleName; - }); - - if (lifecycleIndex === -1) { - return; + const lifecycleIndex = Object.keys(this.statusMap).findIndex((currentLifecycleName) => { + return currentLifecycleName === lifecycleName; + }); + + if (lifecycleIndex === -1) { + return; + } + + const statusMap = _.clone(this.statusMap); + const lifecycleNames = Object.keys(statusMap); + + const finishedLifecycleNames = lifecycleNames.slice(0, lifecycleIndex); + const pendingLifecycleNames = lifecycleNames.slice(lifecycleIndex + 1); + + this.statusMap = lifecycleNames.reduce((result, currentLifecycleName) => { + if (lifecycleName === currentLifecycleName) { + result[currentLifecycleName] = 'running'; + } else if (finishedLifecycleNames.includes(currentLifecycleName)) { + result[currentLifecycleName] = 'finished'; + } else if (pendingLifecycleNames.includes(currentLifecycleName)) { + result[currentLifecycleName] = 'pending'; } + return result; + }, {} as DollieContextStatusMap); - const statusMap = _.clone(this.statusMap); - const lifecycleNames = Object.keys(statusMap); - - const finishedLifecycleNames = lifecycleNames.slice(0, lifecycleIndex); - const pendingLifecycleNames = lifecycleNames.slice(lifecycleIndex + 1); - - this.statusMap = lifecycleNames.reduce((result, currentLifecycleName) => { - if (lifecycleName === currentLifecycleName) { - result[currentLifecycleName] = 'running'; - } else if (finishedLifecycleNames.includes(currentLifecycleName)) { - result[currentLifecycleName] = 'finished'; - } else if (pendingLifecycleNames.includes(currentLifecycleName)) { - result[currentLifecycleName] = 'pending'; - } - return result; - }, {} as DollieContextStatusMap); - - this.statusChangeHandler(this.statusMap); + this.statusChangeHandler(this.statusMap); } private updateFinishedStatus(lifecycleName: string) { - if (_.isString(this.statusMap[lifecycleName])) { - this.statusMap[lifecycleName] = 'finished'; - } + if (_.isString(this.statusMap[lifecycleName])) { + this.statusMap[lifecycleName] = 'finished'; + } } } diff --git a/packages/@dollie/core/src/diff.ts b/packages/@dollie/core/src/diff.ts index 2b8a24d..100815f 100644 --- a/packages/@dollie/core/src/diff.ts +++ b/packages/@dollie/core/src/diff.ts @@ -1,9 +1,11 @@ -import { diffLines } from 'diff'; +import { + diffLines, +} from 'diff'; import _ from 'lodash'; import { - DiffChange, - MergeBlock, - PatchTable, + DiffChange, + MergeBlock, + PatchTable, } from './interfaces'; /** @@ -13,29 +15,29 @@ import { * @returns {DiffChange[]} */ const diff = (originalContent: string, currentContent?: string): DiffChange[] => { - const changes = diffLines(originalContent, currentContent || originalContent); - const result: DiffChange[] = []; - let lineNumber = 0; - - const splitChanges: DiffChange[] = changes.reduce((result, currentItem) => { - const lines = (currentItem.value.endsWith('\n') - ? currentItem.value.slice(0, -1) - : currentItem.value - ).split('\n').map((item) => _.omit({ ...currentItem, value: `${item}\n` }, 'count')); - return result.concat(lines); - }, []); - - while (splitChanges.length !== 0) { - const currentSplitChange = splitChanges.shift(); - result.push({ - ...currentSplitChange, - lineNumber: !currentSplitChange.added - ? lineNumber++ - : lineNumber - 1, - }); - } + const changes = diffLines(originalContent, currentContent || originalContent); + const result: DiffChange[] = []; + let lineNumber = 0; + + const splitChanges: DiffChange[] = changes.reduce((result, currentItem) => { + const lines = (currentItem.value.endsWith('\n') + ? currentItem.value.slice(0, -1) + : currentItem.value + ).split('\n').map((item) => _.omit({ ...currentItem, value: `${item}\n` }, 'count')); + return result.concat(lines); + }, []); + + while (splitChanges.length !== 0) { + const currentSplitChange = splitChanges.shift(); + result.push({ + ...currentSplitChange, + lineNumber: !currentSplitChange.added + ? lineNumber++ + : lineNumber - 1, + }); + } - return result; + return result; }; /** @@ -45,169 +47,169 @@ const diff = (originalContent: string, currentContent?: string): DiffChange[] => * @returns {DiffChange[]} */ const merge = (originalChanges: DiffChange[], diffList: DiffChange[][]): DiffChange[] => { - if (!originalChanges) { - return []; - } - - if (!diffList || !Array.isArray(diffList) || diffList.length === 0) { - return originalChanges; - } - - let originalDiff = Array.from(originalChanges); - const patchTable: PatchTable = {}; - - // traverse diffList and put all lines to `patchTable` - for (const currentDiff of diffList) { - for (const change of currentDiff) { - if (change.added) { - if (!patchTable[change.lineNumber]) { - patchTable[change.lineNumber] = { - changes: [], - modifyLength: 0, - }; - } - patchTable[change.lineNumber].changes.push(change); - } else { - // if current line is removed, then ADD THE REMOVE TAG - // to the same line in `originalDiff` - if (change.removed) { - originalDiff = _.set(originalDiff, [change.lineNumber, 'removed'], true); - } - } + if (!originalChanges) { + return []; + } + + if (!diffList || !Array.isArray(diffList) || diffList.length === 0) { + return originalChanges; + } + + let originalDiff = Array.from(originalChanges); + const patchTable: PatchTable = {}; + + // traverse diffList and put all lines to `patchTable` + for (const currentDiff of diffList) { + for (const change of currentDiff) { + if (change.added) { + if (!patchTable[change.lineNumber]) { + patchTable[change.lineNumber] = { + changes: [], + modifyLength: 0, + }; } - - const addedChangeLineNumbers = currentDiff - .filter((change) => change.added) - .map((change) => change.lineNumber); - - for (const matchedLineNumber of _.uniq(addedChangeLineNumbers)) { - // every time add a line to a same line in original content - // current lineNumber will increase - patchTable[matchedLineNumber].modifyLength += 1; + patchTable[change.lineNumber].changes.push(change); + } else { + // if current line is removed, then ADD THE REMOVE TAG + // to the same line in `originalDiff` + if (change.removed) { + originalDiff = _.set(originalDiff, [change.lineNumber, 'removed'], true); } + } } - for (const patchIndex of Object.keys(patchTable)) { - const currentPatchItem = patchTable[patchIndex]; - // if the times of adding lines in current line more than one - // it should be marked as `conflict` - if (currentPatchItem.modifyLength > 1) { - currentPatchItem.changes = currentPatchItem.changes.map((change) => ({ - ...change, - conflicted: true, - conflictGroup: 'current', - })); - } + const addedChangeLineNumbers = currentDiff + .filter((change) => change.added) + .map((change) => change.lineNumber); + + for (const matchedLineNumber of _.uniq(addedChangeLineNumbers)) { + // every time add a line to a same line in original content + // current lineNumber will increase + patchTable[matchedLineNumber].modifyLength += 1; } + } + + for (const patchIndex of Object.keys(patchTable)) { + const currentPatchItem = patchTable[patchIndex]; + // if the times of adding lines in current line more than one + // it should be marked as `conflict` + if (currentPatchItem.modifyLength > 1) { + currentPatchItem.changes = currentPatchItem.changes.map((change) => ({ + ...change, + conflicted: true, + conflictGroup: 'current', + })); + } + } - const blocks: DiffChange[][] = []; + const blocks: DiffChange[][] = []; - // reduce all lines - const patches = Object.keys(patchTable).map( - (patchIndex) => patchTable[patchIndex], - ); + // reduce all lines + const patches = Object.keys(patchTable).map( + (patchIndex) => patchTable[patchIndex], + ); - const lineNumbers = Object.keys(patchTable).map((lineNumber) => { - return parseInt(lineNumber, 10); - }); + const lineNumbers = Object.keys(patchTable).map((lineNumber) => { + return parseInt(lineNumber, 10); + }); - lineNumbers.unshift(-1); + lineNumbers.unshift(-1); - /** + /** * this step will split all rows into alternating * "original" - "patch" - "original" arrays */ - for (const [index, lineNumber] of lineNumbers.entries()) { - const nextLineNumber = lineNumbers[index + 1]; - if (nextLineNumber === undefined) { - blocks.push(originalDiff.slice(lineNumber + 1)); - } else { - blocks.push(originalDiff.slice(lineNumber + 1, nextLineNumber + 1)); - } + for (const [index, lineNumber] of lineNumbers.entries()) { + const nextLineNumber = lineNumbers[index + 1]; + if (nextLineNumber === undefined) { + blocks.push(originalDiff.slice(lineNumber + 1)); + } else { + blocks.push(originalDiff.slice(lineNumber + 1, nextLineNumber + 1)); } + } - // reduce the blocks and get the merged diff changes - return blocks.reduce((currentResult, currentBlock) => { - const currentPatchItem = patches.shift(); + // reduce the blocks and get the merged diff changes + return blocks.reduce((currentResult, currentBlock) => { + const currentPatchItem = patches.shift(); - const result = currentResult.concat(currentBlock); + const result = currentResult.concat(currentBlock); - if (!currentPatchItem) { - return result; - } + if (!currentPatchItem) { + return result; + } - return result.concat(currentPatchItem.changes); - }, []); + return result.concat(currentPatchItem.changes); + }, []); }; const parseMergeBlocksToText = (blocks: MergeBlock[]): string => { - return blocks.reduce((result, currentBlock) => { - if (currentBlock.status === 'OK') { - return `${result}${currentBlock.values.current.join('')}`; - } else { - return ( - result + - '<<<<<<< former\n' + - currentBlock.values.former.join('') + - '=======\n' + - currentBlock.values.current.join('') + - '>>>>>>> current\n' - ); - } - }, ''); + return blocks.reduce((result, currentBlock) => { + if (currentBlock.status === 'OK') { + return `${result}${currentBlock.values.current.join('')}`; + } else { + return ( + result + + '<<<<<<< former\n' + + currentBlock.values.former.join('') + + '=======\n' + + currentBlock.values.current.join('') + + '>>>>>>> current\n' + ); + } + }, ''); }; const parseDiffToMergeBlocks = (changes: DiffChange[]): MergeBlock[] => { - const mergeBlocks: MergeBlock[] = []; - for (const line of changes) { - if (line.removed) { - continue; - } + const mergeBlocks: MergeBlock[] = []; + for (const line of changes) { + if (line.removed) { + continue; + } - if (line.conflicted) { - if ( - mergeBlocks.length === 0 - || _.last(mergeBlocks).status !== 'CONFLICT' - ) { - mergeBlocks.push({ - status: 'CONFLICT', - values: { - current: [], - former: [], - }, - }); - } - const lastMergeBlock = _.last(mergeBlocks); - lastMergeBlock.values[line.conflictGroup].push(line.value); - } else { - if ( - mergeBlocks.length === 0 - || _.last(mergeBlocks).status === 'CONFLICT' - ) { - mergeBlocks.push({ - status: 'OK', - values: { - former: [], - current: [], - }, - }); - } - const lastMergeBlock = _.last(mergeBlocks); - lastMergeBlock.values.current.push(line.value); - } + if (line.conflicted) { + if ( + mergeBlocks.length === 0 || + _.last(mergeBlocks).status !== 'CONFLICT' + ) { + mergeBlocks.push({ + status: 'CONFLICT', + values: { + current: [], + former: [], + }, + }); + } + const lastMergeBlock = _.last(mergeBlocks); + lastMergeBlock.values[line.conflictGroup].push(line.value); + } else { + if ( + mergeBlocks.length === 0 || + _.last(mergeBlocks).status === 'CONFLICT' + ) { + mergeBlocks.push({ + status: 'OK', + values: { + former: [], + current: [], + }, + }); + } + const lastMergeBlock = _.last(mergeBlocks); + lastMergeBlock.values.current.push(line.value); } + } - return mergeBlocks; + return mergeBlocks; }; const parseFileTextToMergeBlocks = (content: string): MergeBlock[] => { - return parseDiffToMergeBlocks(diff(content)); + return parseDiffToMergeBlocks(diff(content)); }; export { - diff, - merge, - parseMergeBlocksToText, - parseDiffToMergeBlocks, - parseFileTextToMergeBlocks, + diff, + merge, + parseMergeBlocksToText, + parseDiffToMergeBlocks, + parseFileTextToMergeBlocks, }; diff --git a/packages/@dollie/core/src/errors.ts b/packages/@dollie/core/src/errors.ts index ed28221..433a8a3 100644 --- a/packages/@dollie/core/src/errors.ts +++ b/packages/@dollie/core/src/errors.ts @@ -1,41 +1,41 @@ import _ from 'lodash'; class InvalidInputError extends Error { - public constructor(reason) { - super(`Invalid input item${_.isString(reason) ? `: ${reason}` : ''}`); - } + public constructor(reason) { + super(`Invalid input item${_.isString(reason) ? `: ${reason}` : ''}`); + } } class ContextError extends Error { - public constructor(reason) { - super(`Invalid context${_.isString(reason) ? `: ${reason}` : ''}`); - } + public constructor(reason) { + super(`Invalid context${_.isString(reason) ? `: ${reason}` : ''}`); + } } class HTTPNotFoundError extends Error { - public constructor() { - super('Template resource not found'); - } + public constructor() { + super('Template resource not found'); + } } class HTTPTimeoutError extends Error { - public constructor() { - super('Download template resource timed out'); - } + public constructor() { + super('Download template resource timed out'); + } } class DollieError extends Error { - public code: string; + public code: string; - public constructor(message: string) { - super(message); - } + public constructor(message: string) { + super(message); + } } export { - InvalidInputError, - ContextError, - HTTPNotFoundError, - HTTPTimeoutError, - DollieError, + InvalidInputError, + ContextError, + HTTPNotFoundError, + HTTPTimeoutError, + DollieError, }; diff --git a/packages/@dollie/core/src/files.ts b/packages/@dollie/core/src/files.ts index e951762..f499dc5 100644 --- a/packages/@dollie/core/src/files.ts +++ b/packages/@dollie/core/src/files.ts @@ -1,6 +1,6 @@ import { - DeleteConfigHandler, - DollieTemplateConfig, + DeleteConfigHandler, + DollieTemplateConfig, } from './interfaces'; import _ from 'lodash'; @@ -12,50 +12,48 @@ import _ from 'lodash'; * @returns */ const getFileConfigGlobs = async ( - config: DollieTemplateConfig, - // targeted extend templates - targets: string[], - // enum: `merge` or `delete` - type: string, + config: DollieTemplateConfig, + // targeted extend templates + targets: string[], + // enum: `merge` or `delete` + type: string, ): Promise => { - let patterns = ( - _.get(config, `files.${type}`) - || [] - ) as (string | DeleteConfigHandler)[]; + let patterns = ( + _.get(config, `files.${type}`) || [] + ) as (string | DeleteConfigHandler)[]; - for (const target of targets) { - patterns = patterns.concat( - ( - _.get(config, `extendTemplates.${target}.files.${type}`) - || [] - ) as (string | DeleteConfigHandler)[], - ); - } + for (const target of targets) { + patterns = patterns.concat( + ( + _.get(config, `extendTemplates.${target}.files.${type}`) || [] + ) as (string | DeleteConfigHandler)[], + ); + } - let result: string[] = []; + let result: string[] = []; - for (const pattern of patterns) { - if (_.isString(pattern)) { - result.push(pattern); - } else if (_.isFunction(pattern)) { - // get patterns from functional param - const returnValue = await pattern(config, targets); + for (const pattern of patterns) { + if (_.isString(pattern)) { + result.push(pattern); + } else if (_.isFunction(pattern)) { + // get patterns from functional param + const returnValue = await pattern(config, targets); - if (_.isArray(returnValue) && returnValue.length > 0) { - result = result.concat(returnValue); - } else if (_.isString(returnValue)) { - result.push(returnValue); - } - } + if (_.isArray(returnValue) && returnValue.length > 0) { + result = result.concat(returnValue); + } else if (_.isString(returnValue)) { + result.push(returnValue); + } } + } - return _.uniq( - result - .filter((item) => !!item) - .filter((item) => _.isString(item)), - ) as string[]; + return _.uniq( + result + .filter((item) => !!item) + .filter((item) => _.isString(item)), + ) as string[]; }; export { - getFileConfigGlobs, + getFileConfigGlobs, }; diff --git a/packages/@dollie/core/src/generator.ts b/packages/@dollie/core/src/generator.ts index e641ddf..1dfd9f4 100644 --- a/packages/@dollie/core/src/generator.ts +++ b/packages/@dollie/core/src/generator.ts @@ -1,654 +1,665 @@ import { - BinaryTable, - CacheTable, - ConflictBlockMetadata, - DiffChange, - DollieGeneratorConfig, - DollieExtendTemplateConfig, - DollieGeneratorResult, - DollieQuestion, - DollieTemplateCleanUpFunction, - DollieTemplateConfig, - FileSystem, - MergeTable, - TemplatePropsItem, - MessageHandler, + BinaryTable, + CacheTable, + ConflictBlockMetadata, + DiffChange, + DollieGeneratorConfig, + DollieExtendTemplateConfig, + DollieGeneratorResult, + DollieQuestion, + DollieTemplateCleanUpFunction, + DollieTemplateConfig, + FileSystem, + MergeTable, + TemplatePropsItem, + MessageHandler, } from './interfaces'; import _ from 'lodash'; import { - InvalidInputError, - ContextError, + InvalidInputError, + ContextError, } from './errors'; import { - DollieOrigin, + DollieOrigin, } from '@dollie/origins'; -import { Volume } from 'memfs'; import { - loadTemplate as loadTemplateFromOrigin, - readTemplateEntities, + Volume, +} from 'memfs'; +import { + loadTemplate as loadTemplateFromOrigin, + readTemplateEntities, } from './loader'; import path from 'path'; import { - EXTEND_TEMPLATE_LABEL_PREFIX, - EXTEND_TEMPLATE_PATHNAME_PREFIX, - EXTEND_TEMPLATE_PREFIX, - MAIN_TEMPLATE_PATHNAME_PREFIX, - TEMPLATE_CACHE_PATHNAME_PREFIX, - TEMPLATE_CONFIG_FILE_NAMES, - TEMPLATE_FILE_PREFIX, + EXTEND_TEMPLATE_LABEL_PREFIX, + EXTEND_TEMPLATE_PATHNAME_PREFIX, + EXTEND_TEMPLATE_PREFIX, + MAIN_TEMPLATE_PATHNAME_PREFIX, + TEMPLATE_CACHE_PATHNAME_PREFIX, + TEMPLATE_CONFIG_FILE_NAMES, + TEMPLATE_FILE_PREFIX, } from './constants'; import requireFromString from 'require-from-string'; -import { answersParser } from './props'; import { - diff, - merge, - parseDiffToMergeBlocks, - parseFileTextToMergeBlocks, - parseMergeBlocksToText, + answersParser, +} from './props'; +import { + diff, + merge, + parseDiffToMergeBlocks, + parseFileTextToMergeBlocks, + parseMergeBlocksToText, } from './diff'; import ejs from 'ejs'; -import { getFileConfigGlobs } from './files'; -import { GlobMatcher } from './matchers'; -import { createHttpInstance } from './http'; +import { + getFileConfigGlobs, +} from './files'; +import { + GlobMatcher, +} from './matchers'; +import { + createHttpInstance, +} from './http'; class Generator { - // name of template that to be used - public templateName: string; - // selected origin id - public templateOrigin: string; - // virtual file system instance - protected volume: FileSystem; - // template config, read from `dollie.js` or `dollie.json` - protected templateConfig: DollieTemplateConfig = {}; - // the table who stores all files - // key is relative pathname, value is the diff changes - protected cacheTable: CacheTable = {}; - protected mergeTable: MergeTable = {}; - // store binary pathname in virtual file system - protected binaryTable: BinaryTable = {}; - // origins list - protected origins: DollieOrigin[] = []; - private templatePropsList: TemplatePropsItem[] = []; - private pendingTemplateLabels: string[] = []; - private targetedExtendTemplateIds: string[] = []; - // glob pathname matcher - private matcher: GlobMatcher; - private messageHandler: MessageHandler; - - /** - * Generator constructor - * @param {string} projectName name of project that to be generated - * @param {string} templateOriginName origin context id, read by generator - * @param {DollieGeneratorConfig} config generator configuration - */ - public constructor( - protected projectName: string, - private templateOriginName: string, - private config: DollieGeneratorConfig = {}, - ) { - this.templateName = ''; - this.templateOrigin = ''; - this.volume = new Volume(); - this.pendingTemplateLabels.push('main'); - const { onMessage: messageHandler = _.noop } = this.config; - this.messageHandler = messageHandler; + // name of template that to be used + public templateName: string; + // selected origin id + public templateOrigin: string; + // virtual file system instance + protected volume: FileSystem; + // template config, read from `dollie.js` or `dollie.json` + protected templateConfig: DollieTemplateConfig = {}; + // the table who stores all files + // key is relative pathname, value is the diff changes + protected cacheTable: CacheTable = {}; + protected mergeTable: MergeTable = {}; + // store binary pathname in virtual file system + protected binaryTable: BinaryTable = {}; + // origins list + protected origins: DollieOrigin[] = []; + private templatePropsList: TemplatePropsItem[] = []; + private pendingTemplateLabels: string[] = []; + private targetedExtendTemplateIds: string[] = []; + // glob pathname matcher + private matcher: GlobMatcher; + private messageHandler: MessageHandler; + + /** + * Generator constructor + * @param {string} projectName name of project that to be generated + * @param {string} templateOriginName origin context id, read by generator + * @param {DollieGeneratorConfig} config generator configuration + */ + public constructor( + protected projectName: string, + private templateOriginName: string, + private config: DollieGeneratorConfig = {}, + ) { + this.templateName = ''; + this.templateOrigin = ''; + this.volume = new Volume(); + this.pendingTemplateLabels.push('main'); + const { onMessage: messageHandler = _.noop } = this.config; + this.messageHandler = messageHandler; + } + + public checkInputs() { + this.messageHandler('Validating inputs...'); + if (!this.templateOriginName || !_.isString(this.templateOriginName)) { + throw new InvalidInputError('Parameter `name` should be a string'); } - - public checkInputs() { - this.messageHandler('Validating inputs...'); - if (!this.templateOriginName || !_.isString(this.templateOriginName)) { - throw new InvalidInputError('Parameter `name` should be a string'); - } - if (!this.projectName || !_.isString(this.projectName)) { - throw new InvalidInputError('Parameter `projectName` should be a string'); - } + if (!this.projectName || !_.isString(this.projectName)) { + throw new InvalidInputError('Parameter `projectName` should be a string'); } - - public async initialize() { - this.origins = _.get(this, 'config.origins') || []; - - // parse the origin id and template id - if (_.isString(this.templateOriginName)) { - if (!this.templateOriginName.includes(':')) { - this.templateOrigin = 'github'; - this.templateName = this.templateOriginName; - } else { - [ - this.templateOrigin = 'github', - this.templateName, - ] = this.templateOriginName.split(':'); - } - } - - this.messageHandler('Preparing cache directory...'); - this.volume.mkdirSync(TEMPLATE_CACHE_PATHNAME_PREFIX, { recursive: true }); - this.messageHandler('Initialization finished successfully'); + } + + public async initialize() { + this.origins = _.get(this, 'config.origins') || []; + + // parse the origin id and template id + if (_.isString(this.templateOriginName)) { + if (!this.templateOriginName.includes(':')) { + this.templateOrigin = 'github'; + this.templateName = this.templateOriginName; + } else { + [ + this.templateOrigin = 'github', + this.templateName, + ] = this.templateOriginName.split(':'); + } } - public checkContext() { - this.messageHandler('Checking runtime context...'); - const originIds = this.origins.map((origin) => origin.name); - const uniqueOriginIds = _.uniq(originIds); - if (originIds.length > uniqueOriginIds.length) { - throw new ContextError('duplicated origin names'); - } + this.messageHandler('Preparing cache directory...'); + this.volume.mkdirSync(TEMPLATE_CACHE_PATHNAME_PREFIX, { recursive: true }); + this.messageHandler('Initialization finished successfully'); + } + + public checkContext() { + this.messageHandler('Checking runtime context...'); + const originIds = this.origins.map((origin) => origin.name); + const uniqueOriginIds = _.uniq(originIds); + if (originIds.length > uniqueOriginIds.length) { + throw new ContextError('duplicated origin names'); } + } - public async loadTemplate() { - this.messageHandler(`Start downloading template from ${this.templateOrigin}:${this.templateName}`); - - // match and use the correct origin handler function - const origin = this.origins.find((origin) => origin.name === this.templateOrigin); - if (!origin) { - throw new ContextError(`origin name \`${this.templateOrigin}\` not found`); - } - - if (!_.isFunction(origin.handler)) { - throw new ContextError(`origin \`${this.templateOrigin}\` has a wrong handler type`); - } - - // get url and headers from origin handler - const { url, headers } = await origin.handler( - this.templateName, - _.get(this.config, 'origin') || {}, - createHttpInstance(_.get(this.config, 'loader') || {}), - ); - - if (!_.isString(url) || !url) { - throw new ContextError(`origin \`${this.templateOrigin}\` url parsed with errors`); - } - - // download template file to this.volume - const duration = await loadTemplateFromOrigin(url, this.volume, { - headers, - ...({ - timeout: 90000, - }), - ...this.config.loader, - }); - - this.messageHandler(`Template downloaded in ${duration}ms`); - this.messageHandler('Parsing template config...'); - - this.templateConfig = this.parseTemplateConfig(); + public async loadTemplate() { + this.messageHandler(`Start downloading template from ${this.templateOrigin}:${this.templateName}`); - this.messageHandler('Template config parsed successfully'); + // match and use the correct origin handler function + const origin = this.origins.find((origin) => origin.name === this.templateOrigin); + if (!origin) { + throw new ContextError(`origin name \`${this.templateOrigin}\` not found`); + } - return duration; + if (!_.isFunction(origin.handler)) { + throw new ContextError(`origin \`${this.templateOrigin}\` has a wrong handler type`); } - /** - * get all props from main template and each extend template - * @returns {TemplatePropsItem[]} - */ - public async queryAllTemplateProps() { - while (this.pendingTemplateLabels.length !== 0) { - const currentPendingExtendTemplateLabel = this.pendingTemplateLabels.shift(); - if (currentPendingExtendTemplateLabel === 'main') { - await this.getTemplateProps(); - } else if (currentPendingExtendTemplateLabel.startsWith(EXTEND_TEMPLATE_LABEL_PREFIX)) { - await this.getTemplateProps(currentPendingExtendTemplateLabel); - this.targetedExtendTemplateIds.push( - currentPendingExtendTemplateLabel.slice(EXTEND_TEMPLATE_LABEL_PREFIX.length), - ); - } - } + // get url and headers from origin handler + const { url, headers } = await origin.handler( + this.templateName, + _.get(this.config, 'origin') || {}, + createHttpInstance(_.get(this.config, 'loader') || {}), + ); - // get glob file patterns and create matcher - const patterns = await this.generateFilePatterns(); - this.matcher = new GlobMatcher(patterns); + if (!_.isString(url) || !url) { + throw new ContextError(`origin \`${this.templateOrigin}\` url parsed with errors`); + } - return _.clone(this.templatePropsList); + // download template file to this.volume + const duration = await loadTemplateFromOrigin(url, this.volume, { + headers, + ...({ + timeout: 90000, + }), + ...this.config.loader, + }); + + this.messageHandler(`Template downloaded in ${duration}ms`); + this.messageHandler('Parsing template config...'); + + this.templateConfig = this.parseTemplateConfig(); + + this.messageHandler('Template config parsed successfully'); + + return duration; + } + + /** + * get all props from main template and each extend template + * @returns {TemplatePropsItem[]} + */ + public async queryAllTemplateProps() { + while (this.pendingTemplateLabels.length !== 0) { + const currentPendingExtendTemplateLabel = this.pendingTemplateLabels.shift(); + if (currentPendingExtendTemplateLabel === 'main') { + await this.getTemplateProps(); + } else if (currentPendingExtendTemplateLabel.startsWith(EXTEND_TEMPLATE_LABEL_PREFIX)) { + await this.getTemplateProps(currentPendingExtendTemplateLabel); + this.targetedExtendTemplateIds.push( + currentPendingExtendTemplateLabel.slice(EXTEND_TEMPLATE_LABEL_PREFIX.length), + ); + } } - /** - * traverse all files, and get the contents from them - * get the diff changes from the initial content of current file - * and then push diff changes to cacheTable - * @returns {void} - */ - public copyTemplateFileToCacheTable() { - this.messageHandler('Generating template files...'); + // get glob file patterns and create matcher + const patterns = await this.generateFilePatterns(); + this.matcher = new GlobMatcher(patterns); - const mainTemplateProps = this.templatePropsList.find((item) => item.label === 'main') || {}; + return _.clone(this.templatePropsList); + } - if (!mainTemplateProps) { - return; - } + /** + * traverse all files, and get the contents from them + * get the diff changes from the initial content of current file + * and then push diff changes to cacheTable + * @returns {void} + */ + public copyTemplateFileToCacheTable() { + this.messageHandler('Generating template files...'); - const templateIds = ['main'].concat(this.targetedExtendTemplateIds.map((id) => `extend:${id}`)); - - for (const templateId of templateIds) { - const templatePropsItem = this.templatePropsList.find((item) => item.label === templateId); - - if (!templatePropsItem) { - continue; - } - - const { label, props } = templatePropsItem; - let templateStartPathname: string; - - /** - * template label format: - * main template: `main` - * extend template: `extend:{name}` - */ - if (label === 'main') { - templateStartPathname = this.mainTemplatePathname(); - } else if (label.startsWith(EXTEND_TEMPLATE_LABEL_PREFIX)) { - // slice extend template label to get the id of current extend template - const extendTemplateId = label.slice(EXTEND_TEMPLATE_LABEL_PREFIX.length); - templateStartPathname = this.extendTemplatePathname(extendTemplateId); - } - - if (!templateStartPathname) { continue; } - - // traverse template structure and get all entities - const entities = readTemplateEntities(this.volume, templateStartPathname); - - for (const entity of entities) { - const { - absolutePathname, - entityName, - isBinary, - isDirectory, - relativeDirectoryPathname: relativePathname, - } = entity; - - if (isDirectory) { continue; } - - if (isBinary) { - this.binaryTable[`${relativePathname}/${entityName}`] - = this.volume.readFileSync(absolutePathname) as Buffer; - } else { - const fileRawContent = this.volume.readFileSync(absolutePathname).toString(); - let currentFileContent: string; - - // detect if current file is a template file - if (entityName.startsWith(TEMPLATE_FILE_PREFIX)) { - currentFileContent = ejs.render(fileRawContent, _.merge(mainTemplateProps, props)); - } else { - currentFileContent = fileRawContent; - } - - const currentFileName = entityName.startsWith(TEMPLATE_FILE_PREFIX) - ? entityName.slice(TEMPLATE_FILE_PREFIX.length) - : entityName; - const currentFileRelativePathname = `${relativePathname ? `${relativePathname}/` : ''}${currentFileName}`; - - let currentFileDiffChanges: DiffChange[]; - if ( - !this.cacheTable[currentFileRelativePathname] - || this.cacheTable[currentFileRelativePathname].length === 0 - || !_.isArray(this.cacheTable[currentFileRelativePathname]) - ) { - // if cacheTable does not have a record for current file - this.cacheTable[currentFileRelativePathname] = []; - // set initial diff changes - currentFileDiffChanges = diff(currentFileContent); - } else { - const originalFileDiffChanges = this.cacheTable[currentFileRelativePathname][0]; - const originalFileContent = originalFileDiffChanges.map((diffItem) => diffItem.value).join(''); - currentFileDiffChanges = diff(originalFileContent, currentFileContent); - } - - this.cacheTable[currentFileRelativePathname].push(currentFileDiffChanges); - } - } - } - } + const mainTemplateProps = this.templatePropsList.find((item) => item.label === 'main') || {}; - public deleteFiles() { - this.cacheTable = Object.keys(this.cacheTable).reduce((result, pathname) => { - if (!this.matcher.match(pathname, 'delete')) { - result[pathname] = this.cacheTable[pathname]; - } - return result; - }, {} as CacheTable); + if (!mainTemplateProps) { + return; } - public mergeTemplateFiles() { - for (const entityPathname of Object.keys(this.cacheTable)) { - const diffs = this.cacheTable[entityPathname]; - if (!diffs || !_.isArray(diffs) || diffs.length === 0) { - continue; - } - if (this.matcher.match(entityPathname, 'merge')) { - if (diffs.length === 1) { - this.mergeTable[entityPathname] = parseDiffToMergeBlocks(diffs[0]); - } else { - const originalDiffChanges = diffs[0]; - const forwardDiffChangesGroup = diffs.slice(1); - // merge diff changes if current file is written more than once - const mergedDiffChanges = merge(originalDiffChanges, forwardDiffChangesGroup); - this.mergeTable[entityPathname] = parseDiffToMergeBlocks(mergedDiffChanges); - } - } else { - // if current file does not match patterns in `merge` - // then get the content from the last diff changes - this.mergeTable[entityPathname] = parseDiffToMergeBlocks(_.last(diffs)); - } + const templateIds = ['main'].concat(this.targetedExtendTemplateIds.map((id) => `extend:${id}`)); + + for (const templateId of templateIds) { + const templatePropsItem = this.templatePropsList.find((item) => item.label === templateId); + + if (!templatePropsItem) { + continue; + } + + const { label, props } = templatePropsItem; + let templateStartPathname: string; + + /** + * template label format: + * main template: `main` + * extend template: `extend:{name}` + */ + if (label === 'main') { + templateStartPathname = this.mainTemplatePathname(); + } else if (label.startsWith(EXTEND_TEMPLATE_LABEL_PREFIX)) { + // slice extend template label to get the id of current extend template + const extendTemplateId = label.slice(EXTEND_TEMPLATE_LABEL_PREFIX.length); + templateStartPathname = this.extendTemplatePathname(extendTemplateId); + } + + if (!templateStartPathname) { continue; } + + // traverse template structure and get all entities + const entities = readTemplateEntities(this.volume, templateStartPathname); + + for (const entity of entities) { + const { + absolutePathname, + entityName, + isBinary, + isDirectory, + relativeDirectoryPathname: relativePathname, + } = entity; + + if (isDirectory) { continue; } + + if (isBinary) { + this.binaryTable[`${relativePathname}/${entityName}`] + = this.volume.readFileSync(absolutePathname) as Buffer; + } else { + const fileRawContent = this.volume.readFileSync(absolutePathname).toString(); + let currentFileContent: string; + + // detect if current file is a template file + if (entityName.startsWith(TEMPLATE_FILE_PREFIX)) { + currentFileContent = ejs.render(fileRawContent, _.merge(mainTemplateProps, props)); + } else { + currentFileContent = fileRawContent; + } + + const currentFileName = entityName.startsWith(TEMPLATE_FILE_PREFIX) + ? entityName.slice(TEMPLATE_FILE_PREFIX.length) + : entityName; + const currentFileRelativePathname = `${relativePathname ? `${relativePathname}/` : ''}${currentFileName}`; + + let currentFileDiffChanges: DiffChange[]; + if ( + !this.cacheTable[currentFileRelativePathname] || + this.cacheTable[currentFileRelativePathname].length === 0 || + !_.isArray(this.cacheTable[currentFileRelativePathname]) + ) { + // if cacheTable does not have a record for current file + this.cacheTable[currentFileRelativePathname] = []; + // set initial diff changes + currentFileDiffChanges = diff(currentFileContent); + } else { + const originalFileDiffChanges = this.cacheTable[currentFileRelativePathname][0]; + const originalFileContent = originalFileDiffChanges.map((diffItem) => diffItem.value).join(''); + currentFileDiffChanges = diff(originalFileContent, currentFileContent); + } + + this.cacheTable[currentFileRelativePathname].push(currentFileDiffChanges); } + } } - - /** - * check for conflicted blocks and throw them to user to get solved blocks - * @returns {void} - */ - public async resolveConflicts() { - const { conflictsSolver } = this.config; - - if (!_.isFunction(conflictsSolver)) { - return; - } - - const remainedConflictedFileDataList = this.getConflictedFileDataList(); - const totalConflicts = _.clone(remainedConflictedFileDataList); - - while (remainedConflictedFileDataList.length > 0) { - const { pathname, index } = remainedConflictedFileDataList.shift(); - - const currentPathnameConflicts = totalConflicts.filter((item) => { - return item.pathname === pathname; - }); - - const total = currentPathnameConflicts.length; - const currentIndex = currentPathnameConflicts.findIndex((conflict) => { - return index === conflict.index; - }); - - const result = await conflictsSolver({ - pathname, - total, - block: this.mergeTable[pathname][index], - index: currentIndex, - content: parseMergeBlocksToText(this.mergeTable[pathname]), - }); - - /** - * deal with the return value from `conflictsSolver`: - * - null: user skips current block, throw again - * - 'ignored': user ignores current block, continue - * - MergeBlock: user solves current block, overwrite `mergeTable` - */ - if (_.isNull(result)) { - remainedConflictedFileDataList.unshift({ pathname, index }); - } else if (result === 'ignored') { - this.mergeTable[pathname][index] = { - ...this.mergeTable[pathname][index], - ignored: true, - }; - } else if (!_.isEmpty(result)) { - this.mergeTable[pathname][index] = { - ...result, - status: 'OK', - }; - } + } + + public deleteFiles() { + this.cacheTable = Object.keys(this.cacheTable).reduce((result, pathname) => { + if (!this.matcher.match(pathname, 'delete')) { + result[pathname] = this.cacheTable[pathname]; + } + return result; + }, {} as CacheTable); + } + + public mergeTemplateFiles() { + for (const entityPathname of Object.keys(this.cacheTable)) { + const diffs = this.cacheTable[entityPathname]; + if (!diffs || !_.isArray(diffs) || diffs.length === 0) { + continue; + } + if (this.matcher.match(entityPathname, 'merge')) { + if (diffs.length === 1) { + this.mergeTable[entityPathname] = parseDiffToMergeBlocks(diffs[0]); + } else { + const originalDiffChanges = diffs[0]; + const forwardDiffChangesGroup = diffs.slice(1); + // merge diff changes if current file is written more than once + const mergedDiffChanges = merge(originalDiffChanges, forwardDiffChangesGroup); + this.mergeTable[entityPathname] = parseDiffToMergeBlocks(mergedDiffChanges); } + } else { + // if current file does not match patterns in `merge` + // then get the content from the last diff changes + this.mergeTable[entityPathname] = parseDiffToMergeBlocks(_.last(diffs)); + } } + } - public async runCleanups() { - this.messageHandler('Running cleanup functions...'); - - const clonedTables = { - mergeTable: _.clone(this.mergeTable), - binaryTable: _.clone(this.binaryTable), - }; - - const cleanups = (_.get(this.templateConfig, 'cleanups') || []) - .concat(this.targetedExtendTemplateIds.reduce((result, templateId) => { - const currentCleanups = _.get(this.templateConfig, `extendTemplates.${templateId}.cleanups`) || []; - return result.concat(currentCleanups); - }, [])) - .filter((cleanup) => _.isFunction(cleanup)) as DollieTemplateCleanUpFunction[]; - - const addFile = (pathname: string, content: string) => { - if (!clonedTables.mergeTable[pathname]) { - clonedTables.mergeTable[pathname] = parseFileTextToMergeBlocks(content); - } - }; - - const addTextFile = addFile; - - const addBinaryFile = (pathname: string, content: Buffer) => { - if (!clonedTables.binaryTable[pathname]) { - clonedTables.binaryTable[pathname] = content; - } - }; - - const deleteFiles = (pathnameList: string[]) => { - for (const pathname of pathnameList) { - clonedTables.mergeTable[pathname] = null; - clonedTables.binaryTable[pathname] = null; - } - }; + /** + * check for conflicted blocks and throw them to user to get solved blocks + * @returns {void} + */ + public async resolveConflicts() { + const { conflictsSolver } = this.config; - const exists = (pathname: string): boolean => { - return Boolean(this.mergeTable[pathname]); - }; + if (!_.isFunction(conflictsSolver)) { + return; + } - const getTextFileContent = (pathname: string) => { - return parseMergeBlocksToText(this.mergeTable[pathname]); + const remainedConflictedFileDataList = this.getConflictedFileDataList(); + const totalConflicts = _.clone(remainedConflictedFileDataList); + + while (remainedConflictedFileDataList.length > 0) { + const { pathname, index } = remainedConflictedFileDataList.shift(); + + const currentPathnameConflicts = totalConflicts.filter((item) => { + return item.pathname === pathname; + }); + + const total = currentPathnameConflicts.length; + const currentIndex = currentPathnameConflicts.findIndex((conflict) => { + return index === conflict.index; + }); + + const result = await conflictsSolver({ + pathname, + total, + block: this.mergeTable[pathname][index], + index: currentIndex, + content: parseMergeBlocksToText(this.mergeTable[pathname]), + }); + + /** + * deal with the return value from `conflictsSolver`: + * - null: user skips current block, throw again + * - 'ignored': user ignores current block, continue + * - MergeBlock: user solves current block, overwrite `mergeTable` + */ + if (_.isNull(result)) { + remainedConflictedFileDataList.unshift({ pathname, index }); + } else if (result === 'ignored') { + this.mergeTable[pathname][index] = { + ...this.mergeTable[pathname][index], + ignored: true, }; - - const getBinaryFileBuffer = (pathname: string) => { - return this.binaryTable[pathname]; + } else if (!_.isEmpty(result)) { + this.mergeTable[pathname][index] = { + ...result, + status: 'OK', }; - - for (const cleanup of cleanups) { - await cleanup({ - addFile, - addTextFile, - addBinaryFile, - deleteFiles, - exists, - getTextFileContent, - getBinaryFileBuffer, - }); - } - - Object.keys(clonedTables).forEach((tableName) => { - this[tableName] = Object.keys(clonedTables[tableName]).reduce((result, pathname) => { - const content = clonedTables[tableName][pathname]; - if (!_.isNull(content)) { - result[pathname] = content; - } - return result; - }, {}); - }); + } } - - public getResult(): DollieGeneratorResult { - let files = Object.keys(this.mergeTable).reduce((result, pathname) => { - result[pathname] = parseMergeBlocksToText(this.mergeTable[pathname]); - return result; - }, {}); - files = _.merge(this.binaryTable, files); - const conflicts = this.getIgnoredConflictedFilePathnameList(); - this.messageHandler('Generator finished successfully'); - return { files, conflicts }; + } + + public async runCleanups() { + this.messageHandler('Running cleanup functions...'); + + const clonedTables = { + mergeTable: _.clone(this.mergeTable), + binaryTable: _.clone(this.binaryTable), + }; + + const cleanups = (_.get(this.templateConfig, 'cleanups') || []) + .concat(this.targetedExtendTemplateIds.reduce((result, templateId) => { + const currentCleanups = _.get(this.templateConfig, `extendTemplates.${templateId}.cleanups`) || []; + return result.concat(currentCleanups); + }, [])) + .filter((cleanup) => _.isFunction(cleanup)) as DollieTemplateCleanUpFunction[]; + + const addFile = (pathname: string, content: string) => { + if (!clonedTables.mergeTable[pathname]) { + clonedTables.mergeTable[pathname] = parseFileTextToMergeBlocks(content); + } + }; + + const addTextFile = addFile; + + const addBinaryFile = (pathname: string, content: Buffer) => { + if (!clonedTables.binaryTable[pathname]) { + clonedTables.binaryTable[pathname] = content; + } + }; + + const deleteFiles = (pathnameList: string[]) => { + for (const pathname of pathnameList) { + clonedTables.mergeTable[pathname] = null; + clonedTables.binaryTable[pathname] = null; + } + }; + + const exists = (pathname: string): boolean => { + return Boolean(this.mergeTable[pathname]); + }; + + const getTextFileContent = (pathname: string) => { + return parseMergeBlocksToText(this.mergeTable[pathname]); + }; + + const getBinaryFileBuffer = (pathname: string) => { + return this.binaryTable[pathname]; + }; + + for (const cleanup of cleanups) { + await cleanup({ + addFile, + addTextFile, + addBinaryFile, + deleteFiles, + exists, + getTextFileContent, + getBinaryFileBuffer, + }); } - protected mainTemplatePathname(pathname = '') { - return TEMPLATE_CACHE_PATHNAME_PREFIX - + MAIN_TEMPLATE_PATHNAME_PREFIX - + (pathname ? `/${pathname}` : ''); - } - - protected extendTemplatePathname(templateId: string, pathname = '') { - const BASE_PATH = TEMPLATE_CACHE_PATHNAME_PREFIX - + EXTEND_TEMPLATE_PATHNAME_PREFIX; - - if (!templateId) { - return null; + Object.keys(clonedTables).forEach((tableName) => { + this[tableName] = Object.keys(clonedTables[tableName]).reduce((result, pathname) => { + const content = clonedTables[tableName][pathname]; + if (!_.isNull(content)) { + result[pathname] = content; } - - return BASE_PATH - + '/' - + templateId - + (pathname ? `/${pathname}` : ''); + return result; + }, {}); + }); + } + + public getResult(): DollieGeneratorResult { + let files = Object.keys(this.mergeTable).reduce((result, pathname) => { + result[pathname] = parseMergeBlocksToText(this.mergeTable[pathname]); + return result; + }, {}); + files = _.merge(this.binaryTable, files); + const conflicts = this.getIgnoredConflictedFilePathnameList(); + this.messageHandler('Generator finished successfully'); + return { files, conflicts }; + } + + protected mainTemplatePathname(pathname = '') { + return TEMPLATE_CACHE_PATHNAME_PREFIX + + MAIN_TEMPLATE_PATHNAME_PREFIX + + (pathname ? `/${pathname}` : ''); + } + + protected extendTemplatePathname(templateId: string, pathname = '') { + const BASE_PATH = + TEMPLATE_CACHE_PATHNAME_PREFIX + + EXTEND_TEMPLATE_PATHNAME_PREFIX; + + if (!templateId) { + return null; } - private async generateFilePatterns() { - const patterns = {}; - - for (const type of ['merge', 'delete']) { - patterns[type] = await getFileConfigGlobs( - this.templateConfig, - this.targetedExtendTemplateIds, - type, - ); - } - - return patterns; + return BASE_PATH + + '/' + + templateId + + (pathname ? `/${pathname}` : ''); + } + + private async generateFilePatterns() { + const patterns = {}; + + for (const type of ['merge', 'delete']) { + patterns[type] = await getFileConfigGlobs( + this.templateConfig, + this.targetedExtendTemplateIds, + type, + ); } - private getConflictedFileDataList() { - const conflicts: ConflictBlockMetadata[] = []; - - for (const pathname of Object.keys(this.mergeTable)) { - const mergeBlocks = this.mergeTable[pathname]; - for (const [index, mergeBlock] of mergeBlocks.entries()) { - if (mergeBlock.status === 'CONFLICT' && !mergeBlock.ignored) { - conflicts.push({ - pathname, - index, - }); - } - } - } - - return conflicts; - } + return patterns; + } - private getIgnoredConflictedFilePathnameList() { - const result: string[] = []; + private getConflictedFileDataList() { + const conflicts: ConflictBlockMetadata[] = []; - for (const pathname of Object.keys(this.mergeTable)) { - const mergeBlocks = this.mergeTable[pathname]; - for (const mergeBlock of mergeBlocks) { - if (mergeBlock.status === 'CONFLICT') { - result.push(pathname); - } - } + for (const pathname of Object.keys(this.mergeTable)) { + const mergeBlocks = this.mergeTable[pathname]; + for (const [index, mergeBlock] of mergeBlocks.entries()) { + if (mergeBlock.status === 'CONFLICT' && !mergeBlock.ignored) { + conflicts.push({ + pathname, + index, + }); } - - return _.uniq(result); + } } - private async getTemplateProps(extendTemplateLabel = null) { - const { getTemplateProps } = this.config; - const questions = (extendTemplateLabel && _.isString(extendTemplateLabel)) - ? _.get(this.templateConfig, `extendTemplates.${extendTemplateLabel}.questions`) - : _.get(this.templateConfig, 'questions'); - - if (_.isFunction(getTemplateProps)) { - let props = {}; - if (questions && _.isArray(questions) && questions.length > 0) { - const answers = await getTemplateProps(this.templateConfig.questions); - const { props: currentProps = {}, pendingExtendTemplateLabels = [] } = answersParser(answers); - - props = currentProps; - - if (pendingExtendTemplateLabels.length > 0) { - for (const pendingExtendTemplateLabel of pendingExtendTemplateLabels) { - this.pendingTemplateLabels.push(`${EXTEND_TEMPLATE_LABEL_PREFIX}${pendingExtendTemplateLabel}`); - } - } - } - this.templatePropsList.push({ - props, - label: extendTemplateLabel ? extendTemplateLabel : 'main', - }); - } - } + return conflicts; + } - private readTemplateFileBuffer(pathname: string): Buffer { - return this.volume.readFileSync(path.resolve( - TEMPLATE_CACHE_PATHNAME_PREFIX, - pathname, - )) as Buffer; - } + private getIgnoredConflictedFilePathnameList() { + const result: string[] = []; - private checkFile(pathname: string): boolean { - const absolutePathname = path.resolve(TEMPLATE_CACHE_PATHNAME_PREFIX, pathname); - return ( - this.volume.existsSync(absolutePathname) - && this.volume.statSync(absolutePathname).isFile() - ); + for (const pathname of Object.keys(this.mergeTable)) { + const mergeBlocks = this.mergeTable[pathname]; + for (const mergeBlock of mergeBlocks) { + if (mergeBlock.status === 'CONFLICT') { + result.push(pathname); + } + } } - private getTemplateConfig() { - let configFileName: string; + return _.uniq(result); + } - for (const fileName of TEMPLATE_CONFIG_FILE_NAMES) { - if (this.checkFile(fileName)) { - configFileName = fileName; - break; - } - } + private async getTemplateProps(extendTemplateLabel = null) { + const { getTemplateProps } = this.config; + const questions = (extendTemplateLabel && _.isString(extendTemplateLabel)) + ? _.get(this.templateConfig, `extendTemplates.${extendTemplateLabel}.questions`) + : _.get(this.templateConfig, 'questions'); - if (!configFileName) { - return {} as DollieTemplateConfig; - } + if (_.isFunction(getTemplateProps)) { + let props = {}; + if (questions && _.isArray(questions) && questions.length > 0) { + const answers = await getTemplateProps(this.templateConfig.questions); + const { props: currentProps = {}, pendingExtendTemplateLabels = [] } = answersParser(answers); - const dollieConfigFileContent = this.readTemplateFileBuffer(configFileName).toString(); + props = currentProps; - if (configFileName.endsWith('.json')) { - try { - return JSON.parse(dollieConfigFileContent) as DollieTemplateConfig; - } catch { - return {} as DollieTemplateConfig; - } - } else if (configFileName.endsWith('.js')) { - return (requireFromString(dollieConfigFileContent) || {}) as DollieTemplateConfig; - } else { - return {} as DollieTemplateConfig; + if (pendingExtendTemplateLabels.length > 0) { + for (const pendingExtendTemplateLabel of pendingExtendTemplateLabels) { + this.pendingTemplateLabels.push(`${EXTEND_TEMPLATE_LABEL_PREFIX}${pendingExtendTemplateLabel}`); + } } + } + this.templatePropsList.push({ + props, + label: extendTemplateLabel ? extendTemplateLabel : 'main', + }); + } + } + + private readTemplateFileBuffer(pathname: string): Buffer { + return this.volume.readFileSync(path.resolve( + TEMPLATE_CACHE_PATHNAME_PREFIX, + pathname, + )) as Buffer; + } + + private checkFile(pathname: string): boolean { + const absolutePathname = path.resolve(TEMPLATE_CACHE_PATHNAME_PREFIX, pathname); + return ( + this.volume.existsSync(absolutePathname) && + this.volume.statSync(absolutePathname).isFile() + ); + } + + private getTemplateConfig() { + let configFileName: string; + + for (const fileName of TEMPLATE_CONFIG_FILE_NAMES) { + if (this.checkFile(fileName)) { + configFileName = fileName; + break; + } } - private parseTemplateConfig() { - const postfixes: string[] = []; - - const generatePostfix = () => { - const postfix = Math.random().toString(32).slice(2); - if (postfixes.includes(postfix)) { - return generatePostfix(); - } else { - return postfix; - } - }; - - const modifyQuestionName = (questions: DollieQuestion[] = []) => { - return questions.map((question) => { - const { name } = question; - if (name.startsWith(`${EXTEND_TEMPLATE_PREFIX}`) && name.endsWith('$')) { - return { - ...question, - name: `${name}__${generatePostfix()}`, - }; - } - return question; - }); - }; + if (!configFileName) { + return {} as DollieTemplateConfig; + } - const config = this.getTemplateConfig(); - - const extendTemplates = (_.get(config, 'extendTemplates') || {}) as DollieExtendTemplateConfig; - - return { - ...config, - questions: modifyQuestionName(_.get(config, 'questions') || []), - extendTemplates: Object.keys(extendTemplates).reduce((result, templateId) => { - const currentExtendTemplateConfig = extendTemplates[templateId]; - result[templateId] = { - ...currentExtendTemplateConfig, - questions: modifyQuestionName(_.get(currentExtendTemplateConfig, 'questions')), - } as Omit; - return result; - }, {} as DollieExtendTemplateConfig), - } as DollieTemplateConfig; + const dollieConfigFileContent = this.readTemplateFileBuffer(configFileName).toString(); + + if (configFileName.endsWith('.json')) { + try { + return JSON.parse(dollieConfigFileContent) as DollieTemplateConfig; + } catch { + return {} as DollieTemplateConfig; + } + } else if (configFileName.endsWith('.js')) { + return (requireFromString(dollieConfigFileContent) || {}) as DollieTemplateConfig; + } else { + return {} as DollieTemplateConfig; } + } + + private parseTemplateConfig() { + const postfixes: string[] = []; + + const generatePostfix = () => { + const postfix = Math.random().toString(32).slice(2); + if (postfixes.includes(postfix)) { + return generatePostfix(); + } else { + return postfix; + } + }; + + const modifyQuestionName = (questions: DollieQuestion[] = []) => { + return questions.map((question) => { + const { name } = question; + if (name.startsWith(`${EXTEND_TEMPLATE_PREFIX}`) && name.endsWith('$')) { + return { + ...question, + name: `${name}__${generatePostfix()}`, + }; + } + return question; + }); + }; + + const config = this.getTemplateConfig(); + + const extendTemplates = (_.get(config, 'extendTemplates') || {}) as DollieExtendTemplateConfig; + + return { + ...config, + questions: modifyQuestionName(_.get(config, 'questions') || []), + extendTemplates: Object.keys(extendTemplates).reduce((result, templateId) => { + const currentExtendTemplateConfig = extendTemplates[templateId]; + result[templateId] = { + ...currentExtendTemplateConfig, + questions: modifyQuestionName(_.get(currentExtendTemplateConfig, 'questions')), + } as Omit; + return result; + }, {} as DollieExtendTemplateConfig), + } as DollieTemplateConfig; + } } export default Generator; diff --git a/packages/@dollie/core/src/http.ts b/packages/@dollie/core/src/http.ts index 3b6f368..9895056 100644 --- a/packages/@dollie/core/src/http.ts +++ b/packages/@dollie/core/src/http.ts @@ -5,40 +5,40 @@ import { RequestOptions } from './interfaces'; import { URL } from 'url'; const generateGotOptions = (options: RequestOptions): GotOptions => { - const { - httpProxyUrl = '', - httpProxyAuth = '', - ...restOptions - } = options; - - const gotOptions = _.clone(restOptions) as GotOptions; - - if (httpProxyUrl) { - const url = new URL(httpProxyUrl); - const { hostname: host, port } = url; - const proxy: tunnel.ProxyOptions = { - host, - port: parseInt(port, 10), - }; - - if (httpProxyAuth) { - proxy.proxyAuth = httpProxyAuth; - } - - gotOptions.agent = { - http: tunnel.httpOverHttp({ proxy }), - https: tunnel.httpsOverHttp({ proxy }), - }; + const { + httpProxyUrl = '', + httpProxyAuth = '', + ...restOptions + } = options; + + const gotOptions = _.clone(restOptions) as GotOptions; + + if (httpProxyUrl) { + const url = new URL(httpProxyUrl); + const { hostname: host, port } = url; + const proxy: tunnel.ProxyOptions = { + host, + port: parseInt(port, 10), + }; + + if (httpProxyAuth) { + proxy.proxyAuth = httpProxyAuth; } - return gotOptions; + gotOptions.agent = { + http: tunnel.httpOverHttp({ proxy }), + https: tunnel.httpsOverHttp({ proxy }), + }; + } + + return gotOptions; }; const createHttpInstance = (options: RequestOptions): Got => { - return got.extend(generateGotOptions(options)); + return got.extend(generateGotOptions(options)); }; export { - generateGotOptions, - createHttpInstance, + generateGotOptions, + createHttpInstance, }; diff --git a/packages/@dollie/core/src/index.ts b/packages/@dollie/core/src/index.ts index ada50e3..26b560c 100644 --- a/packages/@dollie/core/src/index.ts +++ b/packages/@dollie/core/src/index.ts @@ -1,27 +1,28 @@ import Context from './context'; import { - parseDiffToMergeBlocks, - parseFileTextToMergeBlocks, - parseMergeBlocksToText, + parseDiffToMergeBlocks, + parseFileTextToMergeBlocks, + parseMergeBlocksToText, } from './diff'; export { - Context, - parseDiffToMergeBlocks, - parseFileTextToMergeBlocks, - parseMergeBlocksToText, + Context, + parseDiffToMergeBlocks, + parseFileTextToMergeBlocks, + parseMergeBlocksToText, }; export { - DollieConfig, - ConflictSolveResult, - MergeBlock, - ConflictSolverData, + DollieConfig, + ConflictSolveResult, + MergeBlock, + ConflictSolverData, + LoaderConfig, } from './interfaces'; export default { - Context, - parseDiffToMergeBlocks, - parseFileTextToMergeBlocks, - parseMergeBlocksToText, + Context, + parseDiffToMergeBlocks, + parseFileTextToMergeBlocks, + parseMergeBlocksToText, }; diff --git a/packages/@dollie/core/src/interfaces.ts b/packages/@dollie/core/src/interfaces.ts index 776b655..c505a02 100644 --- a/packages/@dollie/core/src/interfaces.ts +++ b/packages/@dollie/core/src/interfaces.ts @@ -1,31 +1,42 @@ import { - DollieOrigin, - DollieOriginConfig, + DollieOrigin, + DollieOriginConfig, } from '@dollie/origins'; -import { Change } from 'diff'; -import { Volume } from 'memfs'; +import { + Change, +} from 'diff'; +import { + Volume, +} from 'memfs'; import fs from 'fs'; -import { Options as GotOptions } from 'got/dist/source'; -import { Answers as DollieAnswers, DistinctQuestion } from 'inquirer'; -import { DollieError } from './errors'; +import { + Options as GotOptions, +} from 'got'; +import { + Answers as DollieAnswers, + DistinctQuestion, +} from 'inquirer'; +import { + DollieError, +} from './errors'; export type DollieQuestion = DistinctQuestion; export interface DiffChange extends Change { - conflicted?: boolean; - conflictGroup?: 'former' | 'current'; - lineNumber: number; + conflicted?: boolean; + conflictGroup?: 'former' | 'current'; + lineNumber: number; } export interface HttpOptions { - httpProxyUrl?: string; - httpProxyAuth?: string; + httpProxyUrl?: string; + httpProxyAuth?: string; } export type RequestOptions = HttpOptions & GotOptions; export interface LoaderOptions extends HttpOptions { - maximumRetryCount?: number; + maximumRetryCount?: number; } export type LoaderConfig = LoaderOptions & GotOptions; @@ -33,17 +44,17 @@ export type LoaderConfig = LoaderOptions & GotOptions; export type ConflictSolveResult = MergeBlock | 'ignored' | null; export interface DollieGeneratorConfig { - origin?: DollieOriginConfig; - origins?: DollieOrigin[]; - loader?: LoaderConfig; - getTemplateProps?: (questions: DollieQuestion[]) => Promise; - conflictsSolver?: (data: ConflictSolverData) => Promise; - onMessage?: MessageHandler; + origin?: DollieOriginConfig; + origins?: DollieOrigin[]; + loader?: LoaderConfig; + getTemplateProps?: (questions: DollieQuestion[]) => Promise; + conflictsSolver?: (data: ConflictSolverData) => Promise; + onMessage?: MessageHandler; } export interface PatchTableItem { - changes: DiffChange[]; - modifyLength: number; + changes: DiffChange[]; + modifyLength: number; } export type PatchTable = Record; @@ -51,61 +62,61 @@ export type MemFS = typeof Volume.prototype; export type FileSystem = MemFS | typeof fs; export interface TemplateEntity { - absolutePathname: string; - relativePathname: string; - entityName: string; - isBinary: boolean; - isDirectory: boolean; - relativeDirectoryPathname: string; + absolutePathname: string; + relativePathname: string; + entityName: string; + isBinary: boolean; + isDirectory: boolean; + relativeDirectoryPathname: string; } export type DeleteConfigHandler = ( - templateConfig: DollieTemplateConfig, - targets: string[], + templateConfig: DollieTemplateConfig, + targets: string[], ) => Promise; export interface DollieTemplateFileConfig { - merge?: string[]; - delete?: (string | DeleteConfigHandler)[]; + merge?: string[]; + delete?: (string | DeleteConfigHandler)[]; } export interface DollieTemplateCleanupData { - addFile: (pathname: string, content: string) => void; - addTextFile: (pathname: string, content: string) => void; - addBinaryFile: (pathname: string, content: Buffer) => void; - deleteFiles: (pathnameList: string[]) => void; - exists: (pathname: string) => void; - getTextFileContent: (pathname: string) => string; - getBinaryFileBuffer: (pathname: string) => Buffer; + addFile: (pathname: string, content: string) => void; + addTextFile: (pathname: string, content: string) => void; + addBinaryFile: (pathname: string, content: Buffer) => void; + deleteFiles: (pathnameList: string[]) => void; + exists: (pathname: string) => void; + getTextFileContent: (pathname: string) => string; + getBinaryFileBuffer: (pathname: string) => Buffer; } export type DollieTemplateCleanUpFunction = (data: DollieTemplateCleanupData) => MergeTable; export type DollieExtendTemplateConfig = Record>; export interface DollieTemplateConfig { - questions?: DollieQuestion[]; - files?: DollieTemplateFileConfig; - cleanups?: DollieTemplateCleanUpFunction[]; - extendTemplates?: DollieExtendTemplateConfig; + questions?: DollieQuestion[]; + files?: DollieTemplateFileConfig; + cleanups?: DollieTemplateCleanUpFunction[]; + extendTemplates?: DollieExtendTemplateConfig; } export interface ParsedProps { - props: Record; - pendingExtendTemplateLabels: string[]; + props: Record; + pendingExtendTemplateLabels: string[]; } export interface TemplatePropsItem { - label: string; - props: DollieAnswers; + label: string; + props: DollieAnswers; } export interface MergeBlock { - status: 'OK' | 'CONFLICT'; - values: { - former: string[], - current: string[], - }; - ignored?: boolean; + status: 'OK' | 'CONFLICT'; + values: { + former: string[], + current: string[], + }; + ignored?: boolean; } export type CacheTable = Record; @@ -113,29 +124,29 @@ export type MergeTable = Record; export type BinaryTable = Record; export interface ConflictItem { - pathname: string; - blocks: MergeBlock[]; + pathname: string; + blocks: MergeBlock[]; } export interface ConflictBlockMetadata { - pathname: string; - index: number; + pathname: string; + index: number; } export interface ConflictSolverData extends ConflictBlockMetadata { - block: MergeBlock; - content: string; - total: number; + block: MergeBlock; + content: string; + total: number; } export interface DollieGeneratorResult { - files: Record; - conflicts: string[]; + files: Record; + conflicts: string[]; } export type DollieContextStatus = 'pending' | 'running' | 'finished'; export interface DollieContextStatusMap { - [key: string]: DollieContextStatus; + [key: string]: DollieContextStatus; } export type StatusChangeHandler = (status: DollieContextStatusMap) => void; @@ -143,8 +154,8 @@ export type ErrorHandler = (error: DollieError) => void; export type MessageHandler = (message: string) => void; export interface DollieConfig { - generator?: DollieGeneratorConfig; - onStatusChange?: StatusChangeHandler; - onError?: ErrorHandler; - onMessage?: MessageHandler; + generator?: DollieGeneratorConfig; + onStatusChange?: StatusChangeHandler; + onError?: ErrorHandler; + onMessage?: MessageHandler; } diff --git a/packages/@dollie/core/src/loader.ts b/packages/@dollie/core/src/loader.ts index ab9a45c..69581de 100644 --- a/packages/@dollie/core/src/loader.ts +++ b/packages/@dollie/core/src/loader.ts @@ -1,22 +1,30 @@ import { - Options as GotOptions, - RequestError, + Options as GotOptions, + RequestError, } from 'got'; import _ from 'lodash'; import path from 'path'; import decompress from 'decompress'; import { - DollieError, - HTTPNotFoundError, - HTTPTimeoutError, + DollieError, + HTTPNotFoundError, + HTTPTimeoutError, } from './errors'; import fs from 'fs'; import { - TEMPLATE_CACHE_PATHNAME_PREFIX, + TEMPLATE_CACHE_PATHNAME_PREFIX, } from './constants'; -import { FileSystem, LoaderConfig, TemplateEntity } from './interfaces'; -import { isBinaryFileSync } from 'isbinaryfile'; -import { createHttpInstance } from './http'; +import { + FileSystem, + LoaderConfig, + TemplateEntity, +} from './interfaces'; +import { + isBinaryFileSync, +} from 'isbinaryfile'; +import { + createHttpInstance, +} from './http'; /** * download zipped fill from to file system and extract it to file system @@ -26,71 +34,71 @@ import { createHttpInstance } from './http'; * @returns {void} */ const downloadCompressedFile = async ( - url: string, - fileSystem: FileSystem, - options: GotOptions = {}, + url: string, + fileSystem: FileSystem, + options: GotOptions = {}, ) => { - const startTimestamp = Date.now(); - - return new Promise((resolve, reject) => { - // prepare destination path for virtual file system - fileSystem.mkdirSync(TEMPLATE_CACHE_PATHNAME_PREFIX, { - recursive: true, - }); - - const getAbsolutePath = (filePath) => { - const relativePathname = filePath.split('/').slice(1).join('/'); - return path.resolve(TEMPLATE_CACHE_PATHNAME_PREFIX, relativePathname); - }; - - const downloader = createHttpInstance(_.merge( - options || {}, { - isStream: true, - }, - )).stream(url); - - const fileBufferChunks = []; - - downloader.on('error', (error: RequestError) => { - const errorMessage = error.toString(); - if (errorMessage.indexOf('404') !== -1) { - reject(new HTTPNotFoundError()); - } - if (error.code === 'ETIMEDOUT') { - reject(new HTTPTimeoutError()); - } - const otherError = new DollieError(errorMessage); - otherError.code = error.code || 'E_UNKNOWN'; - reject(new Error(errorMessage)); - }); - - downloader.on('data', (chunk) => { - fileBufferChunks.push(chunk); - }); - - downloader.on('end', () => { - // concat all buffer chunks to one buffer - const fileBuffer = Buffer.concat(fileBufferChunks); - - decompress(fileBuffer).then((files) => { - for (const file of files) { - const { type, path: filePath, data } = file; - if (type === 'directory') { - fileSystem.mkdirSync(getAbsolutePath(filePath), { - recursive: true, - }); - } else if (type === 'file') { - fileSystem.writeFileSync(getAbsolutePath(filePath), data, { - encoding: 'utf8', - }); - } - } - return; - }).then(() => { - resolve(Date.now() - startTimestamp); + const startTimestamp = Date.now(); + + return new Promise((resolve, reject) => { + // prepare destination path for virtual file system + fileSystem.mkdirSync(TEMPLATE_CACHE_PATHNAME_PREFIX, { + recursive: true, + }); + + const getAbsolutePath = (filePath) => { + const relativePathname = filePath.split('/').slice(1).join('/'); + return path.resolve(TEMPLATE_CACHE_PATHNAME_PREFIX, relativePathname); + }; + + const downloader = createHttpInstance(_.merge( + options || {}, { + isStream: true, + }, + )).stream(url); + + const fileBufferChunks = []; + + downloader.on('error', (error: RequestError) => { + const errorMessage = error.toString(); + if (errorMessage.indexOf('404') !== -1) { + reject(new HTTPNotFoundError()); + } + if (error.code === 'ETIMEDOUT') { + reject(new HTTPTimeoutError()); + } + const otherError = new DollieError(errorMessage); + otherError.code = error.code || 'E_UNKNOWN'; + reject(new Error(errorMessage)); + }); + + downloader.on('data', (chunk) => { + fileBufferChunks.push(chunk); + }); + + downloader.on('end', () => { + // concat all buffer chunks to one buffer + const fileBuffer = Buffer.concat(fileBufferChunks); + + decompress(fileBuffer).then((files) => { + for (const file of files) { + const { type, path: filePath, data } = file; + if (type === 'directory') { + fileSystem.mkdirSync(getAbsolutePath(filePath), { + recursive: true, + }); + } else if (type === 'file') { + fileSystem.writeFileSync(getAbsolutePath(filePath), data, { + encoding: 'utf8', }); - }); + } + } + return; + }).then(() => { + resolve(Date.now() - startTimestamp); + }); }); + }); }; /** @@ -101,46 +109,46 @@ const downloadCompressedFile = async ( * @returns {void} */ const loadTemplate = async ( + url: string, + fileSystem: FileSystem = fs, + options: LoaderConfig = {}, +) => { + const traverse = async function( url: string, fileSystem: FileSystem = fs, + retries = 0, options: LoaderConfig = {}, -) => { - const traverse = async function( - url: string, - fileSystem: FileSystem = fs, - retries = 0, - options: LoaderConfig = {}, - ) { - const { - maximumRetryCount = 3, - ...originalOptions - } = options; - - try { - return await downloadCompressedFile( - url, - fileSystem, - originalOptions, - ); - } catch (error) { - if (error.code === 'E_TEMPLATE_TIMEOUT' || error instanceof HTTPTimeoutError) { - if (retries < maximumRetryCount) { - return await traverse( - url, - fileSystem, - retries + 1, - options, - ); - } else { - throw new Error(error?.message || 'download template timed out'); - } - } else { - throw error; - } + ) { + const { + maximumRetryCount = 3, + ...originalOptions + } = options; + + try { + return await downloadCompressedFile( + url, + fileSystem, + originalOptions, + ); + } catch (error) { + if (error.code === 'E_TEMPLATE_TIMEOUT' || error instanceof HTTPTimeoutError) { + if (retries < maximumRetryCount) { + return await traverse( + url, + fileSystem, + retries + 1, + options, + ); + } else { + throw new Error(error?.message || 'download template timed out'); } - }; + } else { + throw error; + } + } + }; - return await traverse(url, fileSystem, 0, options); + return await traverse(url, fileSystem, 0, options); }; /** @@ -150,64 +158,64 @@ const loadTemplate = async ( * @returns {TemplateEntity[]} */ const readTemplateEntities = ( - fileSystem: FileSystem = fs, - pathname = TEMPLATE_CACHE_PATHNAME_PREFIX, + fileSystem: FileSystem = fs, + pathname = TEMPLATE_CACHE_PATHNAME_PREFIX, ) => { - /** - * traverse from template root dir - * @param {FileSystem} fileSystem - * @param {string} currentEntityPathname - * @param {TemplateEntity[]} result - * @returns {TemplateEntity[]} - */ - const traverse = ( - fileSystem: FileSystem = fs, - currentEntityPathname: string, - result: TemplateEntity[] = [], - ) => { - let currentResult = Array.from(result); - - if (fileSystem.existsSync(currentEntityPathname)) { - const stat = fileSystem.statSync(currentEntityPathname); - const fileContent = stat.isFile() - ? fileSystem.readFileSync(currentEntityPathname) - : null; - const relativePathname = path.relative(pathname, currentEntityPathname); - - currentResult.push({ - absolutePathname: currentEntityPathname, - relativePathname, - entityName: currentEntityPathname.split('/').pop(), - isBinary: (stat.isFile() && fileContent) - ? isBinaryFileSync(fileContent, fileContent.length) - : false, - isDirectory: stat.isDirectory(), - relativeDirectoryPathname: relativePathname - .split(path.sep) - .slice(0, -1) - .join(path.sep), - }); - - if (stat.isDirectory()) { - const entities = fileSystem.readdirSync(currentEntityPathname); - for (const entity of entities) { - currentResult = traverse( - fileSystem, - `${currentEntityPathname}/${entity}`, - currentResult, - ); - } - } + /** + * traverse from template root dir + * @param {FileSystem} fileSystem + * @param {string} currentEntityPathname + * @param {TemplateEntity[]} result + * @returns {TemplateEntity[]} + */ + const traverse = ( + fileSystem: FileSystem = fs, + currentEntityPathname: string, + result: TemplateEntity[] = [], + ) => { + let currentResult = Array.from(result); + + if (fileSystem.existsSync(currentEntityPathname)) { + const stat = fileSystem.statSync(currentEntityPathname); + const fileContent = stat.isFile() + ? fileSystem.readFileSync(currentEntityPathname) + : null; + const relativePathname = path.relative(pathname, currentEntityPathname); + + currentResult.push({ + absolutePathname: currentEntityPathname, + relativePathname, + entityName: currentEntityPathname.split('/').pop(), + isBinary: (stat.isFile() && fileContent) + ? isBinaryFileSync(fileContent, fileContent.length) + : false, + isDirectory: stat.isDirectory(), + relativeDirectoryPathname: relativePathname + .split(path.sep) + .slice(0, -1) + .join(path.sep), + }); + + if (stat.isDirectory()) { + const entities = fileSystem.readdirSync(currentEntityPathname); + for (const entity of entities) { + currentResult = traverse( + fileSystem, + `${currentEntityPathname}/${entity}`, + currentResult, + ); } + } + } - return currentResult; - }; + return currentResult; + }; - return traverse(fileSystem, pathname); + return traverse(fileSystem, pathname); }; export { - downloadCompressedFile, - loadTemplate, - readTemplateEntities, + downloadCompressedFile, + loadTemplate, + readTemplateEntities, }; diff --git a/packages/@dollie/core/src/matchers.ts b/packages/@dollie/core/src/matchers.ts index cffb5c8..349519e 100644 --- a/packages/@dollie/core/src/matchers.ts +++ b/packages/@dollie/core/src/matchers.ts @@ -2,24 +2,27 @@ import _ from 'lodash'; import minimatch from 'minimatch'; class GlobMatcher { - public constructor( - private readonly patterns: Record, - ) {} + public constructor( + private readonly patterns: Record, + ) {} - public match(pathname: string, type: string) { - const currentPatternsList = this.patterns[type] || []; - if (!_.isArray(currentPatternsList) || currentPatternsList.length === 0) { - return false; - } - for (const pattern of currentPatternsList) { - if (minimatch(pathname, pattern)) { - return true; - } - } - return false; + public match(pathname: string, type: string) { + const currentPatternsList = this.patterns[type] || []; + + if (!_.isArray(currentPatternsList) || currentPatternsList.length === 0) { + return false; + } + + for (const pattern of currentPatternsList) { + if (minimatch(pathname, pattern)) { + return true; + } } + + return false; + } } export { - GlobMatcher, + GlobMatcher, }; diff --git a/packages/@dollie/core/src/props.ts b/packages/@dollie/core/src/props.ts index 0246091..75d8e59 100644 --- a/packages/@dollie/core/src/props.ts +++ b/packages/@dollie/core/src/props.ts @@ -4,43 +4,45 @@ import { EXTEND_TEMPLATE_PREFIX } from './constants'; import { ParsedProps } from './interfaces'; const answersParser = (answers: Answers) => { - return Object.keys(answers).reduce((result, currentKey) => { - const currentProp = answers[currentKey]; - if (currentKey.startsWith('$EXTEND$')) { - if (_.isArray(currentProp)) { - result.pendingExtendTemplateLabels = _ - .uniq(result.pendingExtendTemplateLabels.concat(currentProp)) - .filter((extendTemplateName) => extendTemplateName !== 'null'); - } - if (_.isString(currentProp) && currentProp !== 'null') { - result.pendingExtendTemplateLabels = _.uniq(result.pendingExtendTemplateLabels.concat(currentProp)); - } - } else if (currentKey.startsWith('$EXTEND:')) { - if (_.isBoolean(currentProp) && currentProp) { - const extendTemplateIdChars = []; - for (const char of currentKey.slice(EXTEND_TEMPLATE_PREFIX.length + 1)) { - if (char !== '$') { - extendTemplateIdChars.push(char); - } else { - break; - } - } - if (extendTemplateIdChars.length > 0) { - result.pendingExtendTemplateLabels = _.uniq( - result.pendingExtendTemplateLabels.concat(extendTemplateIdChars.join('')), - ); - } - } - } else { - result.props[currentKey] = currentProp; + return Object.keys(answers).reduce((result, currentKey) => { + const currentProp = answers[currentKey]; + if (currentKey.startsWith('$EXTEND$')) { + if (_.isArray(currentProp)) { + result.pendingExtendTemplateLabels = _ + .uniq(result.pendingExtendTemplateLabels.concat(currentProp)) + .filter((extendTemplateName) => extendTemplateName !== 'null'); + } + if (_.isString(currentProp) && currentProp !== 'null') { + result.pendingExtendTemplateLabels = _.uniq( + result.pendingExtendTemplateLabels.concat(currentProp), + ); + } + } else if (currentKey.startsWith('$EXTEND:')) { + if (_.isBoolean(currentProp) && currentProp) { + const extendTemplateIdChars = []; + for (const char of currentKey.slice(EXTEND_TEMPLATE_PREFIX.length + 1)) { + if (char !== '$') { + extendTemplateIdChars.push(char); + } else { + break; + } } - return result; - }, { - props: {}, - pendingExtendTemplateLabels: [], - } as ParsedProps); + if (extendTemplateIdChars.length > 0) { + result.pendingExtendTemplateLabels = _.uniq( + result.pendingExtendTemplateLabels.concat(extendTemplateIdChars.join('')), + ); + } + } + } else { + result.props[currentKey] = currentProp; + } + return result; + }, { + props: {}, + pendingExtendTemplateLabels: [], + } as ParsedProps); }; export { - answersParser, + answersParser, }; diff --git a/packages/@dollie/core/tsconfig.json b/packages/@dollie/core/tsconfig.json index a75d279..50764ca 100644 --- a/packages/@dollie/core/tsconfig.json +++ b/packages/@dollie/core/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "../../../tsconfig.package.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "outDir": "./lib", - "rootDir": "./src", - "tsBuildInfoFile": "./lib/.tsbuildinfo" - } + "extends": "../../../tsconfig.package.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } } diff --git a/packages/@dollie/origins/package.json b/packages/@dollie/origins/package.json index 0d6fdca..8d54a22 100644 --- a/packages/@dollie/origins/package.json +++ b/packages/@dollie/origins/package.json @@ -1,40 +1,40 @@ { - "name": "@dollie/origins", - "version": "3.0.1", - "description": "Dollie template origins", - "author": "lenconda ", - "homepage": "https://github.com/lenconda/dollie#readme", - "license": "MIT", - "main": "lib/index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/lenconda/dollie.git" - }, - "scripts": { - "build": "rimraf ./lib && tsc", - "build:watch": "tsc --watch", - "test": "echo \"Error: run tests from root\" && exit 1" - }, - "bugs": { - "url": "https://github.com/lenconda/dollie/issues" - }, - "dependencies": { - "got": "^11.8.2", - "lodash": "^4.17.21" - }, - "files": [ - "lib", - "package.json", - "README.md", - "LICENSE" - ], - "publishConfig": { - "access": "public" - }, - "typings": "lib/index.d.ts", - "devDependencies": { - "@types/lodash": "^4.14.170", - "@types/node": "^15.12.2", - "rimraf": "^3.0.2" - } + "name": "@dollie/origins", + "version": "3.0.1", + "description": "Dollie template origins", + "author": "lenconda ", + "homepage": "https://github.com/lenconda/dollie#readme", + "license": "MIT", + "main": "lib/index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/lenconda/dollie.git" + }, + "scripts": { + "build": "rimraf ./lib && tsc", + "build:watch": "tsc --watch", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/lenconda/dollie/issues" + }, + "dependencies": { + "got": "^11.8.2", + "lodash": "^4.17.21" + }, + "files": [ + "lib", + "package.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "typings": "lib/index.d.ts", + "devDependencies": { + "@types/lodash": "^4.14.170", + "@types/node": "^15.12.2", + "rimraf": "^3.0.2" + } } diff --git a/packages/@dollie/origins/src/handlers/github.ts b/packages/@dollie/origins/src/handlers/github.ts index aaa447e..95aff61 100644 --- a/packages/@dollie/origins/src/handlers/github.ts +++ b/packages/@dollie/origins/src/handlers/github.ts @@ -2,18 +2,23 @@ import _ from 'lodash'; import { DollieOriginHandler } from '../interfaces'; export default (async (id, config = {}) => { - if (!id) { - return null; - } + if (!id) { + return null; + } - const [repository, checkout = ''] = id.split('@'); + const [repository, checkout = ''] = id.split('@'); - const token = config.token || ''; + const token = config.token || ''; - return { - url: `https://api.github.com/repos/${repository}/zipball${checkout ? `/${checkout}` : ''}`, - headers: token ? { - 'Authorization': `token ${token}`, - } : {}, - }; + return { + url: 'https://api.github.com/repos/' + + repository + + '/zipball' + + (checkout ? `/${checkout}` : ''), + headers: token + ? { + 'Authorization': `token ${token}`, + } + : {}, + }; }) as DollieOriginHandler; diff --git a/packages/@dollie/origins/src/handlers/gitlab.ts b/packages/@dollie/origins/src/handlers/gitlab.ts index 10bb97a..1ea602d 100644 --- a/packages/@dollie/origins/src/handlers/gitlab.ts +++ b/packages/@dollie/origins/src/handlers/gitlab.ts @@ -3,37 +3,45 @@ import got from 'got'; import { DollieOriginHandler } from '../interfaces'; export default (async (id, config = {}, request = got) => { - if (!id) { - return null; - } + if (!id) { + return null; + } - const { - protocol = 'https', - host = 'gitlab.com', - token = '', - } = config; + const { + protocol = 'https', + host = 'gitlab.com', + token = '', + } = config; - const [repository, checkout = ''] = id.split('@'); - const [repositoryOwner] = id.split('/'); - const headers = token - ? { - 'Private-Token': token, - } - : {}; + const [repository, checkout = ''] = id.split('@'); + const [repositoryOwner] = id.split('/'); + const headers = token + ? { + 'Private-Token': token, + } + : {}; - const res = await request(`${protocol}://${host}/api/v4/users/${repositoryOwner}/projects`, { - timeout: 10000, - retry: 3, - headers, - }); + const res = await request( + `${protocol}://${host}/api/v4/users/${repositoryOwner}/projects`, + { + timeout: 10000, + retry: 3, + headers, + }, + ); - const projects = (JSON.parse(res.body || '[]') || []); - const targetProject = projects.filter((project) => project.path_with_namespace === repository)[0]; + const projects = JSON.parse(res.body || '[]') || []; + const targetProject = projects.filter((project) => { + return project.path_with_namespace === repository; + })[0]; - if (!targetProject) { return null; } + if (!targetProject) { return null; } - return { - headers, - url: `https://gitlab.com/api/v4/projects/${targetProject.id}/repository/archive.zip${checkout ? `?sha=${checkout}` : ''}`, - }; + return { + headers, + url: 'https://gitlab.com/api/v4/projects/' + + targetProject.id + + '/repository/archive.zip' + + (checkout ? `?sha=${checkout}` : ''), + }; }) as DollieOriginHandler; diff --git a/packages/@dollie/origins/src/index.ts b/packages/@dollie/origins/src/index.ts index fa6009d..9c837e2 100644 --- a/packages/@dollie/origins/src/index.ts +++ b/packages/@dollie/origins/src/index.ts @@ -2,19 +2,19 @@ import githubOrigin from './handlers/github'; import gitlabOrigin from './handlers/gitlab'; export { - githubOrigin, - gitlabOrigin, + githubOrigin, + gitlabOrigin, }; export { - DollieOriginConfig, - DollieOriginHeaders, - DollieOriginInfo, - DollieOriginHandler, - DollieOrigin, - DollieOriginMap, + DollieOriginConfig, + DollieOriginHeaders, + DollieOriginInfo, + DollieOriginHandler, + DollieOrigin, + DollieOriginMap, } from './interfaces'; export { - loadOrigins, + loadOrigins, } from './loader'; diff --git a/packages/@dollie/origins/src/interfaces.ts b/packages/@dollie/origins/src/interfaces.ts index 88642a3..2616a88 100644 --- a/packages/@dollie/origins/src/interfaces.ts +++ b/packages/@dollie/origins/src/interfaces.ts @@ -1,21 +1,23 @@ -import { Got } from 'got'; +import { + Got, +} from 'got'; export type DollieOriginConfig = Record; export type DollieOriginHeaders = Record; export type DollieOriginMap = Record; export interface DollieOriginInfo { - url: string; - headers?: DollieOriginHeaders; + url: string; + headers?: DollieOriginHeaders; } export type DollieOriginHandler = ( - id: string, - config: DollieOriginConfig, - request: Got, + id: string, + config: DollieOriginConfig, + request: Got, ) => Promise; export interface DollieOrigin { - name: string; - handler: DollieOriginHandler; + name: string; + handler: DollieOriginHandler; }; diff --git a/packages/@dollie/origins/src/loader.ts b/packages/@dollie/origins/src/loader.ts index 39c00dc..9f8a0fd 100644 --- a/packages/@dollie/origins/src/loader.ts +++ b/packages/@dollie/origins/src/loader.ts @@ -1,8 +1,8 @@ import got from 'got'; import _ from 'lodash'; import { - DollieOrigin, - DollieOriginMap, + DollieOrigin, + DollieOriginMap, } from './interfaces'; import fs from 'fs'; import requireFromString from 'require-from-string'; @@ -10,69 +10,69 @@ import { githubOrigin, gitlabOrigin } from '.'; import path from 'path'; const isUrl = (url: string) => { - return /^(https?:\/\/(([a-zA-Z0-9]+-?)+[a-zA-Z0-9]+\.)+[a-zA-Z]+)(:\d+)?(\/.*)?(\?.*)?(#.*)?$/.test(url); + return /^(https?:\/\/(([a-zA-Z0-9]+-?)+[a-zA-Z0-9]+\.)+[a-zA-Z]+)(:\d+)?(\/.*)?(\?.*)?(#.*)?$/.test(url); }; const loadOrigins = async (config: DollieOriginMap): Promise => { - const result: DollieOrigin[] = []; + const result: DollieOrigin[] = []; - for (const name of Object.keys(config)) { - const pathnameOrHandler = config[name]; + for (const name of Object.keys(config)) { + const pathnameOrHandler = config[name]; - if (_.isFunction(pathnameOrHandler)) { - result.push({ - name, - handler: pathnameOrHandler, - }); - } else if (_.isString(pathnameOrHandler)) { - try { - let content: string; + if (_.isFunction(pathnameOrHandler)) { + result.push({ + name, + handler: pathnameOrHandler, + }); + } else if (_.isString(pathnameOrHandler)) { + try { + let content: string; - if (isUrl(pathnameOrHandler)) { - content = (await got(pathnameOrHandler)).body; - } else { - const originHandlerFilePathname = path.resolve(process.cwd(), pathnameOrHandler); - if (!fs.existsSync(originHandlerFilePathname)) { - continue; - } - const stat = fs.statSync(originHandlerFilePathname); - if (stat.isFile()) { - content = fs.readFileSync(originHandlerFilePathname).toString(); - } - } + if (isUrl(pathnameOrHandler)) { + content = (await got(pathnameOrHandler)).body; + } else { + const originHandlerFilePathname = path.resolve(process.cwd(), pathnameOrHandler); + if (!fs.existsSync(originHandlerFilePathname)) { + continue; + } + const stat = fs.statSync(originHandlerFilePathname); + if (stat.isFile()) { + content = fs.readFileSync(originHandlerFilePathname).toString(); + } + } - if (!content || !_.isString(content)) { - continue; - } + if (!content || !_.isString(content)) { + continue; + } - const handlerFunc = requireFromString(content); + const handlerFunc = requireFromString(content); - if (!_.isFunction(handlerFunc)) { continue; } + if (!_.isFunction(handlerFunc)) { continue; } - result.push({ - name, - handler: handlerFunc, - }); - } catch { - continue; - } - } else { - continue; - } + result.push({ + name, + handler: handlerFunc, + }); + } catch { + continue; + } + } else { + continue; } + } - return [ - { - name: 'github', - handler: githubOrigin, - }, - { - name: 'gitlab', - handler: gitlabOrigin, - }, - ].concat(result); + return [ + { + name: 'github', + handler: githubOrigin, + }, + { + name: 'gitlab', + handler: gitlabOrigin, + }, + ].concat(result); }; export { - loadOrigins, + loadOrigins, }; diff --git a/packages/@dollie/origins/tsconfig.json b/packages/@dollie/origins/tsconfig.json index a75d279..50764ca 100644 --- a/packages/@dollie/origins/tsconfig.json +++ b/packages/@dollie/origins/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "../../../tsconfig.package.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "outDir": "./lib", - "rootDir": "./src", - "tsBuildInfoFile": "./lib/.tsbuildinfo" - } + "extends": "../../../tsconfig.package.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "tsBuildInfoFile": "./lib/.tsbuildinfo" + } } diff --git a/scripts/check.js b/scripts/check.js index b8cff1c..fb0ed15 100644 --- a/scripts/check.js +++ b/scripts/check.js @@ -3,31 +3,31 @@ const fs = require('fs'); const path = require('path'); const getChecksum = (startPath) => { - let result = []; - if (!/(\/?)node_modules|(\/?)lib|(\/?)\.git(\/)|(\/?)\.umi(\/)|(\/?)\.idea(\/)|(\/?)\.vscode(\/)|(\/?)lib(\/)/.test(startPath)) { - console.log(`Checking: ${startPath}`); - const currentStartPath = path.resolve(__dirname, '..', startPath); - const stat = fs.statSync(currentStartPath); - if (stat.isFile()) { - result.push( - `${path.relative(path.resolve(__dirname, '..'), startPath)} ${md5.sync( - currentStartPath, - )}`, - ); - } else if (stat.isDirectory()) { - const entities = fs.readdirSync(currentStartPath); - entities.forEach((entity) => { - result = result.concat(getChecksum(`${startPath}/${entity}`)); - }); - } + let result = []; + if (!/(\/?)node_modules|(\/?)lib|(\/?)\.git(\/)|(\/?)\.umi(\/)|(\/?)\.idea(\/)|(\/?)\.vscode(\/)|(\/?)lib(\/)/.test(startPath)) { + console.log(`Checking: ${startPath}`); + const currentStartPath = path.resolve(__dirname, '..', startPath); + const stat = fs.statSync(currentStartPath); + if (stat.isFile()) { + result.push( + `${path.relative(path.resolve(__dirname, '..'), startPath)} ${md5.sync( + currentStartPath, + )}`, + ); + } else if (stat.isDirectory()) { + const entities = fs.readdirSync(currentStartPath); + entities.forEach((entity) => { + result = result.concat(getChecksum(`${startPath}/${entity}`)); + }); } - return result; + } + return result; }; fs.writeFileSync( - path.resolve(__dirname, '../checksum.txt'), - getChecksum('.').join('\n'), - { - encoding: 'utf-8', - }, + path.resolve(__dirname, '../checksum.txt'), + getChecksum('.').join('\n'), + { + encoding: 'utf-8', + }, ); diff --git a/scripts/docs.js b/scripts/docs.js index bc5f7bc..fb760aa 100644 --- a/scripts/docs.js +++ b/scripts/docs.js @@ -2,8 +2,8 @@ const path = require('path'); const deploy = require('aliyun-oss-deploy'); deploy(path.resolve(__dirname, '../docs-dist'), { - accessKeyId: process.env.ACCESS_KEY_ID, - accessKeySecret: process.env.ACCESS_KEY_SECRET, - region: 'oss-ap-northeast-1', - bucket: 'dollie', + accessKeyId: process.env.ACCESS_KEY_ID, + accessKeySecret: process.env.ACCESS_KEY_SECRET, + region: 'oss-ap-northeast-1', + bucket: 'dollie', }); diff --git a/scripts/publish.js b/scripts/publish.js index 3000dfe..2e4f3ed 100644 --- a/scripts/publish.js +++ b/scripts/publish.js @@ -6,26 +6,26 @@ const path = require('path'); const execSync = require('child_process').execSync; function publish(pkg, directory) { - console.log('[PUBLISH]', pkg); + console.log('[PUBLISH]', pkg); - execSync('npm publish --access public', { - cwd: directory, - encoding: 'utf-8', - stdio: 'inherit', - }); + execSync('npm publish --access public', { + cwd: directory, + encoding: 'utf-8', + stdio: 'inherit', + }); } const BASE_PATH = path.join(__dirname, '../packages/@dollie'); const NPM_TOKEN = process.env.NPM_TOKEN; if (!NPM_TOKEN) { - console.log('No npm token found'); + console.log('No npm token found'); } const packages = fs.readdirSync(BASE_PATH); for (const packageName of packages) { - const packagePathname = path.join(BASE_PATH, packageName); - fs.writeFileSync(path.join(packagePathname, '.npmrc'), `//registry.npmjs.org/:_authToken=${NPM_TOKEN}`); - publish(packageName, packagePathname); + const packagePathname = path.join(BASE_PATH, packageName); + fs.writeFileSync(path.join(packagePathname, '.npmrc'), `//registry.npmjs.org/:_authToken=${NPM_TOKEN}`); + publish(packageName, packagePathname); } diff --git a/tsconfig.json b/tsconfig.json index cac6697..783e71a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,16 @@ { - "files": [], - "compilerOptions": { - "allowJs": true + "files": [], + "compilerOptions": { + "allowJs": true + }, + "references": [{ + "path": "packages/@dollie/cli" }, - "references": [{ - "path": "packages/@dollie/cli" - }, - { - "path": "packages/@dollie/core" - }, - { - "path": "packages/@dollie/origins" - } - ] + { + "path": "packages/@dollie/core" + }, + { + "path": "packages/@dollie/origins" + } + ] } diff --git a/tsconfig.package.json b/tsconfig.package.json index 96d8af4..0e59fba 100644 --- a/tsconfig.package.json +++ b/tsconfig.package.json @@ -1,37 +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" + "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" + ] }