mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
22 Commits
v1.7.0-dev
...
v1.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d62eeeb8e4 | ||
|
|
565d1f33aa | ||
|
|
9f58fff2cf | ||
| dbba81d053 | |||
| 9f5716e1b9 | |||
|
|
4cbb79bb3b | ||
| 98ee00031f | |||
| 8c3369a00d | |||
| 20ef0cea9b | |||
|
|
3413151358 | ||
|
|
d2a2fda1b1 | ||
|
|
486c07a55b | ||
|
|
de05bca546 | ||
|
|
72d4cf58c5 | ||
|
|
2cf31e827e | ||
|
|
3714e8b3bd | ||
|
|
77be7c09d5 | ||
|
|
1246b51cda | ||
|
|
fea6c0fb16 | ||
|
|
2b7423993b | ||
|
|
4868925511 | ||
|
|
79cfdbed38 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -2,7 +2,7 @@ name: Test & Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, develop, feat/*, fix/*]
|
||||
branches: [ master, develop, pre, feat/*, fix/* ]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -14,9 +14,6 @@
|
||||
"variabletoken"
|
||||
],
|
||||
"[markdown]": {
|
||||
"editor.rulers": [
|
||||
87,
|
||||
100
|
||||
],
|
||||
},
|
||||
"editor.rulers": [87, 100]
|
||||
}
|
||||
}
|
||||
|
||||
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@@ -14,7 +14,7 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"command": "yarn typedoc --watch",
|
||||
"command": "pnpm typedoc --watch",
|
||||
"label": "typedoc --watch",
|
||||
"type": "shell",
|
||||
"problemMatcher": []
|
||||
@@ -32,8 +32,8 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"command": "yarn test --watchAll",
|
||||
"label": "yarn test --watchAll",
|
||||
"command": "pnpm test --watchAll",
|
||||
"label": "pnpm test --watchAll",
|
||||
"type": "shell",
|
||||
"problemMatcher": []
|
||||
},
|
||||
|
||||
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,42 +1,48 @@
|
||||
# Change Log
|
||||
|
||||
## [1.7.0-develop.6](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.5...v1.7.0-develop.6) (2023-05-27)
|
||||
## [1.7.2](https://github.com/chenasraf/simple-scaffold/compare/v1.7.1...v1.7.2) (2023-08-20)
|
||||
|
||||
## [1.7.0-develop.5](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.4...v1.7.0-develop.5) (2023-05-12)
|
||||
|
||||
## [1.7.0-develop.4](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.3...v1.7.0-develop.4) (2023-05-11)
|
||||
### Bug Fixes
|
||||
|
||||
## [1.7.0-develop.3](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.2...v1.7.0-develop.3) (2023-05-11)
|
||||
* windows path resolution ([98ee000](https://github.com/chenasraf/simple-scaffold/commit/98ee00031fc1ad67a53797a9e28e5c4759bc8bce))
|
||||
|
||||
## [1.7.0-develop.2](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.1...v1.7.0-develop.2) (2023-05-10)
|
||||
## [1.7.2-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.1...v1.7.2-pre.1) (2023-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* windows path resolution ([98ee000](https://github.com/chenasraf/simple-scaffold/commit/98ee00031fc1ad67a53797a9e28e5c4759bc8bce))
|
||||
|
||||
## [1.7.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0...v1.7.1) (2023-06-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- local config file load error
|
||||
([2b74239](https://github.com/chenasraf/simple-scaffold/commit/2b7423993be06b2375631642455c801ae2acf75f))
|
||||
|
||||
## [1.7.0](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.5...v1.7.0) (2023-05-17)
|
||||
|
||||
## [1.7.0-develop.7](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.6...v1.7.0-develop.7) (2023-06-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- local config file load error
|
||||
([2b74239](https://github.com/chenasraf/simple-scaffold/commit/2b7423993be06b2375631642455c801ae2acf75f))
|
||||
|
||||
## [1.7.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.6.0...v1.7.0-develop.1) (2023-05-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* function config file ([02a8ba1](https://github.com/chenasraf/simple-scaffold/commit/02a8ba16cd6ee31806532845cb5ddbe0f5abf7de))
|
||||
|
||||
- function config file
|
||||
([02a8ba1](https://github.com/chenasraf/simple-scaffold/commit/02a8ba16cd6ee31806532845cb5ddbe0f5abf7de))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use path.normalize ([565090a](https://github.com/chenasraf/simple-scaffold/commit/565090a951e13dd222f2f802df717e7cb6ca0a73))
|
||||
- use path.normalize
|
||||
([565090a](https://github.com/chenasraf/simple-scaffold/commit/565090a951e13dd222f2f802df717e7cb6ca0a73))
|
||||
|
||||
## [1.6.0](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0...v1.6.0) (2023-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* node.js function for remote configs ([ce5adbe](https://github.com/chenasraf/simple-scaffold/commit/ce5adbe0f898a86db6046d7f66d83dfcaa519ad2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move dependency to dev dependency ([d916d88](https://github.com/chenasraf/simple-scaffold/commit/d916d88384054e6c6b40e6299073f1d1acb4d29d))
|
||||
|
||||
## Change Log
|
||||
|
||||
# [1.5.0](https://github.com/chenasraf/simple-scaffold/compare/v1.6.0-develop.1...v1.5.0) (2023-05-04)
|
||||
## [1.6.0](https://github.com/chenasraf/simple-scaffold/compare/v1.6.0-develop.1...v1.6.0) (2023-05-05)
|
||||
|
||||
## [1.6.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0...v1.6.0-develop.1) (2023-05-04)
|
||||
|
||||
|
||||
5
examples/.dotdir/README.md
Executable file
5
examples/.dotdir/README.md
Executable file
@@ -0,0 +1,5 @@
|
||||
# {{ name }} Readme
|
||||
|
||||
TO DO:
|
||||
|
||||
- [ ] ...
|
||||
@@ -195,7 +195,7 @@ export default {
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
verbose: true,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
6
nodemon.json
Normal file
6
nodemon.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"],
|
||||
"watch": ["src"],
|
||||
"exec": "node -r tsconfig-paths/register -r ts-node/register ./src/index.ts",
|
||||
"ext": "ts, js"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "1.7.0-develop.6",
|
||||
"version": "1.7.2",
|
||||
"description": "Generate any file structure - from single components to entire app boilerplates, with a single command.",
|
||||
"homepage": "https://chenasraf.github.io/simple-scaffold",
|
||||
"repository": "https://github.com/chenasraf/simple-scaffold.git",
|
||||
@@ -8,7 +8,7 @@
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"bin": "cmd.js",
|
||||
"packageManager": "pnpm@8.5.0",
|
||||
"packageManager": "pnpm@8.6.2",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"cli",
|
||||
@@ -37,7 +37,7 @@
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"glob": "^10.2.3",
|
||||
"glob": "^10.3.3",
|
||||
"handlebars": "^4.7.7",
|
||||
"massarg": "^1.0.7-pre.1"
|
||||
},
|
||||
@@ -49,6 +49,7 @@
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/semantic-release": "^20.0.1",
|
||||
"conventional-changelog": "^3.1.25",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"conventional-changelog-conventionalcommits": "^5.0.0",
|
||||
@@ -59,7 +60,7 @@
|
||||
"semantic-release-conventional-commits": "^3.0.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.24.6",
|
||||
"typedoc": "^0.24.7",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
3431
pnpm-lock.yaml
generated
3431
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -12,15 +12,7 @@ const releaseRules = [
|
||||
|
||||
/** @type {import('semantic-release').Options} */
|
||||
module.exports = {
|
||||
branches: [
|
||||
"+([0-9])?(.{+([0-9]),x}).x",
|
||||
"master",
|
||||
"next",
|
||||
"next-major",
|
||||
{ name: "develop", prerelease: true },
|
||||
{ name: "beta", prerelease: true },
|
||||
{ name: "alpha", prerelease: true },
|
||||
],
|
||||
branches: ["+([0-9])?(.{+([0-9]),x}).x", "master", "next", "next-major", { name: "pre", prerelease: true }],
|
||||
analyzeCommits: {
|
||||
path: "semantic-release-conventional-commits",
|
||||
majorTypes: releaseRules.filter((x) => x.release === "major").map((x) => x.type),
|
||||
|
||||
@@ -3,8 +3,8 @@ import massarg from "massarg"
|
||||
import chalk from "chalk"
|
||||
import { LogLevel, ScaffoldCmdConfig } from "./types"
|
||||
import { Scaffold } from "./scaffold"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import path from "node:path"
|
||||
import fs from "node:fs/promises"
|
||||
import { parseAppendData, parseConfig } from "./config"
|
||||
|
||||
export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import path from "path"
|
||||
import path from "node:path"
|
||||
import {
|
||||
AsyncResolver,
|
||||
ConfigLoadConfig,
|
||||
FileResponse,
|
||||
FileResponseHandler,
|
||||
LogConfig,
|
||||
LogLevel,
|
||||
Resolver,
|
||||
ScaffoldCmdConfig,
|
||||
ScaffoldConfig,
|
||||
ScaffoldConfigFile,
|
||||
ScaffoldConfigMap,
|
||||
} from "./types"
|
||||
import { OptionsBase } from "massarg/types"
|
||||
import { spawn } from "node:child_process"
|
||||
import os from "node:os"
|
||||
import { handlebarsParse } from "./parser"
|
||||
import { log } from "./logger"
|
||||
import { resolve } from "./utils"
|
||||
import { resolve, wrapNoopResolver } from "./utils"
|
||||
import { getGitConfig } from "./git"
|
||||
|
||||
export function getOptionValueForFile<T>(
|
||||
config: ScaffoldConfig,
|
||||
@@ -58,9 +53,14 @@ export async function parseConfig(config: ScaffoldCmdConfig & OptionsBase): Prom
|
||||
}
|
||||
|
||||
if (config.config) {
|
||||
const { configFile, key } = parseConfigSelection(config.config, config.key)
|
||||
const { configFile, key, isRemote } = parseConfigSelection(config.config, config.key)
|
||||
log(config, LogLevel.Info, `Loading config from ${configFile} with key ${key}`)
|
||||
const configPromise = await getConfig({ config: configFile, quiet: config.quiet, verbose: config.verbose })
|
||||
const configPromise = await getConfig({
|
||||
config: configFile,
|
||||
isRemote,
|
||||
quiet: config.quiet,
|
||||
verbose: config.verbose,
|
||||
})
|
||||
const configImport = await resolve(configPromise, config)
|
||||
|
||||
if (!configImport[key]) {
|
||||
@@ -81,7 +81,10 @@ export async function parseConfig(config: ScaffoldCmdConfig & OptionsBase): Prom
|
||||
return c
|
||||
}
|
||||
|
||||
export function parseConfigSelection(config: string, key?: string): { configFile: string; key: string } {
|
||||
export function parseConfigSelection(
|
||||
config: string,
|
||||
key?: string,
|
||||
): { configFile: string; key: string; isRemote: boolean } {
|
||||
const isUrl = config.includes("://")
|
||||
|
||||
const hasColonToken = (!isUrl && config.includes(":")) || (isUrl && count(config, ":") > 1)
|
||||
@@ -90,7 +93,7 @@ export function parseConfigSelection(config: string, key?: string): { configFile
|
||||
? [config.substring(0, colonIndex), config.substring(colonIndex + 1)]
|
||||
: [config, undefined]
|
||||
const _key = (key ?? templateKey) || "default"
|
||||
return { configFile, key: _key }
|
||||
return { configFile, key: _key, isRemote: isUrl }
|
||||
}
|
||||
|
||||
export function githubPartToUrl(part: string): string {
|
||||
@@ -101,25 +104,17 @@ export function githubPartToUrl(part: string): string {
|
||||
return gitUrl.toString()
|
||||
}
|
||||
|
||||
function wrapNoopResolver<T, R = T>(value: Resolver<T, R>): Resolver<T, R> {
|
||||
if (typeof value === "function") {
|
||||
return value
|
||||
}
|
||||
|
||||
return (_) => value
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getConfig(config: ConfigLoadConfig): Promise<ScaffoldConfigFile> {
|
||||
const { config: configFile, ...logConfig } = config as Required<typeof config>
|
||||
const url = new URL(configFile)
|
||||
const { config: configFile, isRemote, ...logConfig } = config as Required<typeof config>
|
||||
|
||||
if (url.protocol === "file:") {
|
||||
if (!isRemote) {
|
||||
log(logConfig, LogLevel.Info, `Loading config from file ${configFile}`)
|
||||
const absolutePath = path.resolve(process.cwd(), configFile)
|
||||
return wrapNoopResolver(import(absolutePath))
|
||||
}
|
||||
|
||||
const url = new URL(configFile)
|
||||
const isHttp = url.protocol === "http:" || url.protocol === "https:"
|
||||
const isGit = url.protocol === "git:" || (isHttp && url.pathname.endsWith(".git"))
|
||||
|
||||
@@ -134,44 +129,6 @@ export async function getConfig(config: ConfigLoadConfig): Promise<ScaffoldConfi
|
||||
return wrapNoopResolver(import(path.resolve(process.cwd(), configFile)))
|
||||
}
|
||||
|
||||
async function getGitConfig(
|
||||
url: URL,
|
||||
logConfig: LogConfig,
|
||||
): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
|
||||
const repoUrl = `${url.protocol}//${url.host}${url.pathname}`
|
||||
|
||||
log(logConfig, LogLevel.Info, `Cloning git repo ${repoUrl}`)
|
||||
|
||||
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const clone = spawn("git", ["clone", "--depth", "1", repoUrl, tmpPath])
|
||||
|
||||
clone.on("error", reject)
|
||||
clone.on("close", async (code) => {
|
||||
if (code === 0) {
|
||||
log(logConfig, LogLevel.Info, `Loading config from git repo: ${repoUrl}`)
|
||||
const hashPath = url.hash?.replace("#", "") || "scaffold.config.js"
|
||||
const absolutePath = path.resolve(tmpPath, hashPath)
|
||||
const loadedConfig = (await import(absolutePath)).default as ScaffoldConfigMap
|
||||
log(logConfig, LogLevel.Info, `Loaded config from git`)
|
||||
log(logConfig, LogLevel.Debug, `Raw config:`, loadedConfig)
|
||||
const fixedConfig: ScaffoldConfigMap = Object.fromEntries(
|
||||
Object.entries(loadedConfig).map(([k, v]) => [
|
||||
k,
|
||||
// use absolute paths for template as config is necessarily in another directory
|
||||
{ ...v, templates: v.templates.map((t) => path.resolve(tmpPath, t)) },
|
||||
]),
|
||||
)
|
||||
|
||||
resolve(wrapNoopResolver(fixedConfig))
|
||||
} else {
|
||||
reject(new Error(`Git clone failed with code ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function count(string: string, substring: string): number {
|
||||
return string.split(substring).length - 1
|
||||
}
|
||||
|
||||
21
src/file.ts
21
src/file.ts
@@ -1,17 +1,20 @@
|
||||
import path from "path"
|
||||
import { F_OK } from "constants"
|
||||
import path from "node:path"
|
||||
import { F_OK } from "node:constants"
|
||||
import { LogLevel, ScaffoldConfig } from "./types"
|
||||
import { promises as fsPromises } from "fs"
|
||||
const { stat, access, mkdir } = fsPromises
|
||||
import fs from "node:fs/promises"
|
||||
import { glob, hasMagic } from "glob"
|
||||
import { log } from "./logger"
|
||||
import { getOptionValueForFile } from "./config"
|
||||
import { handlebarsParse } from "./parser"
|
||||
import { handleErr } from "./utils"
|
||||
|
||||
const { readFile, writeFile } = fsPromises
|
||||
const { stat, access, mkdir, readFile, writeFile } = fs
|
||||
|
||||
export async function createDirIfNotExists(dir: string, config: ScaffoldConfig): Promise<void> {
|
||||
if (config.dryRun) {
|
||||
log(config, LogLevel.Info, `Dry Run. Not creating dir ${dir}`)
|
||||
return
|
||||
}
|
||||
const parentDir = path.dirname(dir)
|
||||
|
||||
if (!(await pathExists(parentDir))) {
|
||||
@@ -50,7 +53,7 @@ export async function isDir(path: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
export function removeGlob(template: string): string {
|
||||
return template.replace(/\*/g, "").replace(/(\/\/|\\\\)/g, path.sep)
|
||||
return path.normalize(template.replace(/\*/g, ""))
|
||||
}
|
||||
|
||||
export function makeRelativePath(str: string): string {
|
||||
@@ -65,6 +68,8 @@ export function getBasePath(relPath: string): string {
|
||||
}
|
||||
|
||||
export async function getFileList(_config: ScaffoldConfig, template: string): Promise<string[]> {
|
||||
template = template.replaceAll(/[\\]+/g, "/")
|
||||
log(_config, LogLevel.Debug, `Getting file list for ${template}`)
|
||||
return (
|
||||
await glob(template, {
|
||||
dot: true,
|
||||
@@ -89,8 +94,8 @@ export async function getTemplateGlobInfo(config: ScaffoldConfig, template: stri
|
||||
let nonGlobTemplate = isGlob ? removeGlob(template) : template
|
||||
nonGlobTemplate = path.normalize(nonGlobTemplate)
|
||||
const isDirOrGlob = isGlob ? true : await isDir(template)
|
||||
log(config, LogLevel.Debug, "after isDir", isDirOrGlob)
|
||||
const _shouldAddGlob = !isGlob && isDirOrGlob
|
||||
log(config, LogLevel.Debug, "after", { isDirOrGlob, _shouldAddGlob })
|
||||
const origTemplate = template
|
||||
if (_shouldAddGlob) {
|
||||
_template = path.join(template, "**", "*")
|
||||
@@ -148,7 +153,7 @@ export async function copyFileTransformed(
|
||||
log(config, LogLevel.Info, "Done.")
|
||||
} else {
|
||||
log(config, LogLevel.Info, "Dry Run. Output should be:")
|
||||
log(config, LogLevel.Info, finalOutputContents)
|
||||
log(config, LogLevel.Info, finalOutputContents.toString())
|
||||
}
|
||||
} else if (exists) {
|
||||
log(config, LogLevel.Info, `File ${outputPath} already exists, skipping`)
|
||||
|
||||
45
src/git.ts
Normal file
45
src/git.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import path from "node:path"
|
||||
import os from "node:os"
|
||||
import { log } from "./logger"
|
||||
import { AsyncResolver, LogConfig, LogLevel, ScaffoldCmdConfig, ScaffoldConfigMap } from "./types"
|
||||
import { spawn } from "node:child_process"
|
||||
import { wrapNoopResolver } from "./utils"
|
||||
|
||||
export async function getGitConfig(
|
||||
url: URL,
|
||||
logConfig: LogConfig,
|
||||
): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
|
||||
const repoUrl = `${url.protocol}//${url.host}${url.pathname}`
|
||||
|
||||
log(logConfig, LogLevel.Info, `Cloning git repo ${repoUrl}`)
|
||||
|
||||
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const clone = spawn("git", ["clone", "--depth", "1", repoUrl, tmpPath])
|
||||
|
||||
clone.on("error", reject)
|
||||
clone.on("close", async (code) => {
|
||||
if (code === 0) {
|
||||
log(logConfig, LogLevel.Info, `Loading config from git repo: ${repoUrl}`)
|
||||
const hashPath = url.hash?.replace("#", "") || "scaffold.config.js"
|
||||
const absolutePath = path.resolve(tmpPath, hashPath)
|
||||
const loadedConfig = (await import(absolutePath)).default as ScaffoldConfigMap
|
||||
log(logConfig, LogLevel.Info, `Loaded config from git`)
|
||||
log(logConfig, LogLevel.Debug, `Raw config:`, loadedConfig)
|
||||
const fixedConfig: ScaffoldConfigMap = Object.fromEntries(
|
||||
Object.entries(loadedConfig).map(([k, v]) => [
|
||||
k,
|
||||
// use absolute paths for template as config is necessarily in another directory
|
||||
{ ...v, templates: v.templates.map((t) => path.resolve(tmpPath, t)) },
|
||||
]),
|
||||
)
|
||||
|
||||
resolve(wrapNoopResolver(fixedConfig))
|
||||
return
|
||||
}
|
||||
|
||||
reject(new Error(`Git clone failed with code ${code}`))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import path from "path"
|
||||
import path from "node:path"
|
||||
import { DefaultHelpers, Helper, LogLevel, ScaffoldConfig } from "./types"
|
||||
import Handlebars from "handlebars"
|
||||
import dtAdd from "date-fns/add"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* See [readme](README.md)
|
||||
*/
|
||||
import path from "path"
|
||||
import path from "node:path"
|
||||
import { handleErr, resolve } from "./utils"
|
||||
import {
|
||||
isDir,
|
||||
@@ -67,6 +67,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
|
||||
_template,
|
||||
)
|
||||
const files = await getFileList(config, template)
|
||||
log(config, LogLevel.Debug, "Iterating files", { files, template })
|
||||
for (const inputFilePath of files) {
|
||||
if (await isDir(inputFilePath)) {
|
||||
continue
|
||||
@@ -109,7 +110,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
|
||||
* @category Main
|
||||
* @return {Promise<void>} A promise that resolves when the scaffold is complete
|
||||
*/
|
||||
Scaffold.fromConfig = async function(
|
||||
Scaffold.fromConfig = async function (
|
||||
/** The path or URL to the config file */
|
||||
pathOrUrl: string,
|
||||
/** Information needed before loading the config */
|
||||
|
||||
@@ -376,7 +376,7 @@ export type AsyncResolver<T, R = T> = Resolver<T, Promise<R> | R>
|
||||
export type LogConfig = Pick<ScaffoldConfig, "quiet" | "verbose">
|
||||
|
||||
/** @internal */
|
||||
export type ConfigLoadConfig = LogConfig & Pick<ScaffoldCmdConfig, "config">
|
||||
export type ConfigLoadConfig = LogConfig & Pick<ScaffoldCmdConfig, "config"> & { isRemote: boolean }
|
||||
|
||||
/** @internal */
|
||||
export type MinimalConfig = Pick<ScaffoldCmdConfig, "name" | "key">
|
||||
|
||||
@@ -7,3 +7,11 @@ export function handleErr(err: NodeJS.ErrnoException | null): void {
|
||||
export function resolve<T, R = T>(resolver: Resolver<T, R>, arg: T): R {
|
||||
return typeof resolver === "function" ? (resolver as (value: T) => R)(arg) : (resolver as R)
|
||||
}
|
||||
|
||||
export function wrapNoopResolver<T, R = T>(value: Resolver<T, R>): Resolver<T, R> {
|
||||
if (typeof value === "function") {
|
||||
return value
|
||||
}
|
||||
|
||||
return (_) => value
|
||||
}
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import { ScaffoldCmdConfig } from "../src/types"
|
||||
import { OptionsBase } from "massarg/types"
|
||||
import { githubPartToUrl, parseAppendData, parseConfigSelection } from "../src/config"
|
||||
import * as config from "../src/config"
|
||||
import { resolve } from "../src/utils"
|
||||
// @ts-ignore
|
||||
import * as configFile from "../scaffold.config"
|
||||
|
||||
jest.mock("../src/git", () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual("../src/git"),
|
||||
getGitConfig: () => {
|
||||
return Promise.resolve({ default: blankCliConf })
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const { githubPartToUrl, parseAppendData, parseConfig, parseConfigSelection } = config
|
||||
|
||||
const blankCliConf: ScaffoldCmdConfig & OptionsBase = {
|
||||
verbose: 0,
|
||||
@@ -34,6 +49,7 @@ describe("config", () => {
|
||||
expect(parseAppendData('key="value test"', blankCliConf)).toEqual({ key: "value test", name: "test" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("githubPartToUrl", () => {
|
||||
test("works", () => {
|
||||
expect(githubPartToUrl("chenasraf/simple-scaffold")).toEqual("https://github.com/chenasraf/simple-scaffold.git")
|
||||
@@ -42,20 +58,95 @@ describe("config", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("parseConfigSelection", () => {
|
||||
test("works", () => {
|
||||
test("no key", () => {
|
||||
expect(parseConfigSelection("scaffold.config.js")).toEqual({
|
||||
configFile: "scaffold.config.js",
|
||||
key: "default",
|
||||
isRemote: false,
|
||||
})
|
||||
})
|
||||
test("separate key", () => {
|
||||
expect(parseConfigSelection("scaffold.config.js", "component")).toEqual({
|
||||
configFile: "scaffold.config.js",
|
||||
key: "component",
|
||||
isRemote: false,
|
||||
})
|
||||
})
|
||||
test("key override", () => {
|
||||
expect(parseConfigSelection("scaffold.config.js:component", "main")).toEqual({
|
||||
configFile: "scaffold.config.js",
|
||||
key: "main",
|
||||
isRemote: false,
|
||||
})
|
||||
})
|
||||
test("isRemote: false", () => {
|
||||
expect(parseConfigSelection("scaffold.config.js", "main")).toEqual({
|
||||
configFile: "scaffold.config.js",
|
||||
key: "main",
|
||||
isRemote: false,
|
||||
})
|
||||
})
|
||||
test("isRemote: true", () => {
|
||||
expect(
|
||||
parseConfigSelection("https://github.com/chenasraf/simple-scaffold.git#scaffold.config.js:component", "main"),
|
||||
).toEqual({
|
||||
configFile: "https://github.com/chenasraf/simple-scaffold.git#scaffold.config.js",
|
||||
key: "main",
|
||||
isRemote: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("parseConfig", () => {
|
||||
test("normal config does not change", async () => {
|
||||
expect(
|
||||
await parseConfig({
|
||||
...blankCliConf,
|
||||
}),
|
||||
).toEqual(blankCliConf)
|
||||
})
|
||||
describe("appendData", () => {
|
||||
test("appends", async () => {
|
||||
const result = await parseConfig({
|
||||
...blankCliConf,
|
||||
appendData: { key: "value" },
|
||||
})
|
||||
expect(result?.data?.key).toEqual("value")
|
||||
})
|
||||
test("overwrites existing value", async () => {
|
||||
const result = await parseConfig({
|
||||
...blankCliConf,
|
||||
data: { num: "123" },
|
||||
appendData: { num: "1234" },
|
||||
})
|
||||
expect(result?.data?.num).toEqual("1234")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("getConfig", () => {
|
||||
test("gets git config", async () => {
|
||||
const resultFn = await config.getConfig({
|
||||
config: "https://github.com/chenasraf/simple-scaffold.git",
|
||||
isRemote: true,
|
||||
quiet: true,
|
||||
verbose: 0,
|
||||
})
|
||||
const result = await resolve(resultFn, blankCliConf)
|
||||
expect(result).toEqual({ default: blankCliConf })
|
||||
})
|
||||
|
||||
test("gets local file config", async () => {
|
||||
const resultFn = await config.getConfig({
|
||||
config: "scaffold.config.js",
|
||||
isRemote: false,
|
||||
quiet: true,
|
||||
verbose: 0,
|
||||
})
|
||||
const result = await resolve(resultFn, {} as any)
|
||||
expect(result).toEqual(configFile)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ScaffoldCmdConfig, ScaffoldConfig } from "../src/types"
|
||||
import path from "path"
|
||||
import path from "node:path"
|
||||
import * as dateFns from "date-fns"
|
||||
import { OptionsBase } from "massarg/types"
|
||||
import { dateHelper, defaultHelpers, handlebarsParse, nowHelper } from "../src/parser"
|
||||
@@ -38,8 +38,8 @@ describe("parser", () => {
|
||||
Object.defineProperty(path, "sep", { value: origSep })
|
||||
})
|
||||
test("should work for windows paths", async () => {
|
||||
expect(handlebarsParse(blankConf, "C:\\exports\\{{name}}.txt", { isPath: true })).toEqual(
|
||||
Buffer.from("C:\\exports\\test.txt"),
|
||||
expect(handlebarsParse(blankConf, "C:\\exports\\{{name}}.txt", { isPath: true }).toString()).toEqual(
|
||||
"C:\\exports\\test.txt",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "commonjs",
|
||||
@@ -9,7 +10,10 @@
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": false
|
||||
"removeComments": false,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/index.ts", "src/cmd.ts"],
|
||||
"exclude": ["tests/*"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const path = require("path")
|
||||
const path = require("node:path")
|
||||
|
||||
/** @type {import('typedoc').TypeDocOptions} */
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user