diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 3b57e1a..9d88924 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bun install - - run: bun lint + - run: bun check - run: bun types - run: bun test - name: 📢 Release diff --git a/cli.ts b/cli.ts index e87793e..0e575b9 100755 --- a/cli.ts +++ b/cli.ts @@ -1,14 +1,14 @@ #!/usr/bin/env bun +import { collectVariables } from './collect-variables' import { cachePath } from './config' -import { collectVariables } from './utility/collect-variables' -import { downloadTemplate } from './utility/download-template' -import { getConfig } from './utility/get-config' -import { cleanup, getDestinationPath, validatePackageName } from './utility/helper' -import { installDependencies } from './utility/install-dependencies' -import { loadPackage } from './utility/load-package' -import { log } from './utility/log' -import { getTemplateDirectory } from './utility/template-directory' -import { writeFiles } from './utility/write-files' +import { downloadTemplate } from './download-template' +import { getConfig } from './get-config' +import { cleanup, getDestinationPath, validatePackageName } from './helper' +import { installDependencies } from './install-dependencies' +import { loadPackage } from './load-package' +import { log } from './log' +import { getTemplateDirectory } from './template-directory' +import { writeFiles } from './write-files' // Remove additional parameter when used with flag like --yes in older node versions. if (process.argv[2] === 'now') { diff --git a/utility/collect-variables.ts b/collect-variables.ts similarity index 96% rename from utility/collect-variables.ts rename to collect-variables.ts index b0f7bbd..87666d0 100644 --- a/utility/collect-variables.ts +++ b/collect-variables.ts @@ -1,5 +1,5 @@ -import type { Config } from '../types' import { promptVariables } from './prompt' +import type { Config } from './types' const removePropertyFromPrompts = (config: Config, property: string) => { if (config.prompts && Array.isArray(config.prompts)) { diff --git a/decompress.ts b/decompress.ts new file mode 100644 index 0000000..bf88b7f --- /dev/null +++ b/decompress.ts @@ -0,0 +1,158 @@ +import { mkdirSync, readFileSync } from 'node:fs' +import { link, mkdir, readlink, realpath, symlink, utimes, writeFile } from 'node:fs/promises' +import { dirname, join } from 'node:path' +// @ts-ignore +import decompressTar from 'decompress-tar' +// @ts-ignore +import decompressTarbz2 from 'decompress-tarbz2' +// @ts-ignore +import decompressTargz from 'decompress-targz' +// @ts-ignore +import decompressUnzip from 'decompress-unzip' +// @ts-ignore +import stripDirs from 'strip-dirs' + +interface PathFile { + path: string + mode: number + type: string + mtime: Date + linkname: string + data: string +} + +interface Options { + strip: number + filter?: () => boolean + map?: (value: PathFile) => PathFile + plugins: ((input: Buffer, options: Options) => string)[] +} + +function runPlugins(input: Buffer, options: Options) { + if (options.plugins.length === 0) { + return Promise.resolve([]) + } + + return Promise.all(options.plugins.map((x) => x(input, options))).then((files) => files.reduce((a, b) => a.concat(b))) +} + +function safeMakeDir(directory: string, realOutputPath: string): Promise { + return realpath(directory) + .catch((_) => { + const parent = dirname(directory) + return safeMakeDir(parent, realOutputPath) + }) + .then((realParentPath) => { + if (realParentPath.indexOf(realOutputPath) !== 0) { + throw new Error('Refusing to create a directory outside the output path.') + } + + mkdirSync(directory, { recursive: true }) + return directory + }) +} + +const preventWritingThroughSymlink = (destination: string, realOutputPath: string) => { + return readlink(destination) + .catch((_) => { + // Either no file exists, or it's not a symlink. In either case, this is + // not an escape we need to worry about in this phase. + return null + }) + .then(() => { + // No symlink exists at `destination`, so we can continue + return realOutputPath + }) +} + +const extractFile = (input: Buffer, output: string, options: Options) => + runPlugins(input, options).then((input) => { + let files: PathFile[] = input as PathFile[] + if (options.strip > 0) { + files = files + .map((x) => { + x.path = stripDirs(x.path, options.strip) + return x + }) + .filter((x) => x.path !== '.') + } + + if (typeof options.filter === 'function') { + files = files.filter(options.filter) + } + + if (typeof options.map === 'function') { + files = files.map(options.map) + } + + if (!output) { + return files + } + + return Promise.all( + files.map((x) => { + const dest = join(output, x.path) + const mode = x.mode & ~process.umask() + const now = new Date() + + if (x.type === 'directory') { + mkdir(output, { recursive: true }) + + return realpath(output) + .then((realOutputPath) => safeMakeDir(dest, realOutputPath)) + .then(() => utimes(dest, now, x.mtime)) + .then(() => x) + } + + mkdir(output, { recursive: true }) + + return realpath(output) + .then((realOutputPath) => { + // Attempt to ensure parent directory exists (failing if it's outside the output dir) + return safeMakeDir(dirname(dest), realOutputPath).then(() => realOutputPath) + }) + .then((realOutputPath) => { + if (x.type === 'file') { + return preventWritingThroughSymlink(dest, realOutputPath) + } + + return realOutputPath + }) + .then((realOutputPath) => { + return realpath(dirname(dest)).then((realDestinationDir) => { + if (realDestinationDir.indexOf(realOutputPath) !== 0) { + throw new Error(`Refusing to write outside output directory: ${realDestinationDir}`) + } + }) + }) + .then(() => { + if (x.type === 'link') { + return link(x.linkname, dest) + } + + if (x.type === 'symlink' && process.platform === 'win32') { + return link(x.linkname, dest) + } + + if (x.type === 'symlink') { + return symlink(x.linkname, dest) + } + + return writeFile(dest, x.data, { mode }) + }) + .then(() => { + if (x.type === 'file') { + utimes(dest, now, x.mtime) + } + }) + }), + ) + }) + +export function decompress(inputZipFile: string, destinationDirectory: string) { + if (typeof inputZipFile !== 'string' && !Buffer.isBuffer(inputZipFile)) { + return Promise.reject(new TypeError('Input file required')) + } + const options: Options = { strip: 0, plugins: [decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()] } + return extractFile(readFileSync(inputZipFile), destinationDirectory, options) +} diff --git a/download-template.ts b/download-template.ts new file mode 100644 index 0000000..b4e1580 --- /dev/null +++ b/download-template.ts @@ -0,0 +1,106 @@ +import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import axios from 'axios' +import { decompress } from './decompress' +import { log } from './log' + +async function downloadFile(url: string, extractDirectory: string) { + if (!existsSync(extractDirectory)) { + mkdirSync(extractDirectory, { recursive: true }) + } + + const zipFilePath = join(process.cwd(), `temp-${Math.floor(Math.random() * (999 - 1 + 1) + 1)}.zip`) + const body = await axios.get(url, { + responseType: 'arraybuffer', + }) + + writeFileSync(zipFilePath, Buffer.from(body.data)) + await decompress(zipFilePath, extractDirectory) + unlinkSync(zipFilePath) + + return false +} + +// Custom implementation of the 'download-git-repo' package. +export async function download(url: string, destination: string) { + const respository = normalize(url) + const fullUrl = respository.url || getUrl(respository) + + if (!url) { + log('Repository not found', 'error') + return + } + + const error = await downloadFile(fullUrl, destination) + + if (error) { + log(`Failed to download repository contents from ${fullUrl}`, 'error') + return + } + + return true +} + +const normalize = (url: string) => { + let regex = /^(?:(direct):([^#]+)(?:#(.+))?)$/ + let match = regex.exec(url) + + if (match) { + // TODO customizable branch. + const [_, , url, directCheckout = 'main'] = match + return { + type: 'direct', + url, + checkout: directCheckout, + } + } + + regex = /^(?:(github|gitlab|bitbucket):)?(?:(.+):)?([^/]+)\/([^#]+)(?:#(.+))?$/ + match = regex.exec(url) + const [_, type = 'github', origin, owner, name, checkout = 'main'] = match ?? [] + return { + type, + origin: origin ?? (type === 'github' ? 'github.com' : type === 'gitlab' ? 'gitlab.com' : 'bitbucket.org'), + owner, + name, + checkout, + } +} + +function addProtocol(origin: string) { + if (!/^(f|ht)tps?:\/\//i.test(origin)) { + return `https://${origin}` + } + + return origin +} + +function getUrl(repository: ReturnType) { + let url = '' + + // Get origin with protocol and add trailing slash or colon (for ssh) + let origin = addProtocol(repository.origin as string) + if (/^git@/i.test(origin)) { + origin = `${origin}:` + } else { + origin = `${origin}/` + } + + if (repository.type === 'github') { + url = `${origin + repository.owner}/${repository.name}/archive/${repository.checkout}.zip` + } else if (repository.type === 'gitlab') { + url = `${origin + repository.owner}/${repository.name}/repository/archive.zip?ref=${repository.checkout}` + } else if (repository.type === 'bitbucket') { + url = `${origin + repository.owner}/${repository.name}/get/${repository.checkout}.zip` + } + + return url +} + +export async function downloadTemplate(url: string, cachePath: string) { + const success = await download(url, cachePath) + + if (!success) { + log(`Couldn't download repository from ${url}`, 'error') + } +} diff --git a/utility/get-config.ts b/get-config.ts similarity index 92% rename from utility/get-config.ts rename to get-config.ts index fad7152..5683522 100644 --- a/utility/get-config.ts +++ b/get-config.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync } from 'node:fs' import { join } from 'node:path' -import type { Config } from '../types' import { log } from './log' +import type { Config } from './types' export const getConfig = (templateDirectory: string) => { const configFilePath = join(templateDirectory, 'template.json') diff --git a/utility/helper.ts b/helper.ts similarity index 100% rename from utility/helper.ts rename to helper.ts diff --git a/index.ts b/index.ts index 42f9877..c353f81 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,13 @@ +import { collectVariables } from './collect-variables' import { cachePath } from './config' -import { collectVariables } from './utility/collect-variables' -import { downloadTemplate } from './utility/download-template' -import { getConfig } from './utility/get-config' -import { cleanup, getDestinationPath, validatePackageName } from './utility/helper' -import { installDependencies } from './utility/install-dependencies' -import { loadPackage } from './utility/load-package' -import { log } from './utility/log' -import { getTemplateDirectory } from './utility/template-directory' -import { writeFiles } from './utility/write-files' +import { downloadTemplate } from './download-template' +import { getConfig } from './get-config' +import { cleanup, getDestinationPath, validatePackageName } from './helper' +import { installDependencies } from './install-dependencies' +import { loadPackage } from './load-package' +import { log } from './log' +import { getTemplateDirectory } from './template-directory' +import { writeFiles } from './write-files' export const create = async (packageName: string, destinationPath?: string, template = 'default', variableArguments?: object) => { validatePackageName(packageName) diff --git a/utility/install-dependencies.ts b/install-dependencies.ts similarity index 96% rename from utility/install-dependencies.ts rename to install-dependencies.ts index 7854af1..d5c2b09 100644 --- a/utility/install-dependencies.ts +++ b/install-dependencies.ts @@ -1,8 +1,8 @@ import { execSync } from 'node:child_process' import { existsSync, readFileSync } from 'node:fs' import { join } from 'node:path' -import type { Config } from '../types' import { log } from './log' +import type { Config } from './types' export const installDependencies = (config: Config, destination: string) => { if (config.noInstall) { diff --git a/utility/load-package.ts b/load-package.ts similarity index 100% rename from utility/load-package.ts rename to load-package.ts diff --git a/utility/log.ts b/log.ts similarity index 100% rename from utility/log.ts rename to log.ts diff --git a/package.json b/package.json index 802b317..bc53b26 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,15 @@ "license": "MIT", "author": "Matthias Giger", "scripts": { - "format": "biome format . --write", - "lint": "biome lint .", + "check": "biome check --apply .", "types": "tsc" }, "dependencies": { - "download-git-repo": "^3.0.2", + "axios": "^1.6.8", + "decompress-tar": "^4.1.1", + "decompress-tarbz2": "^4.1.1", + "decompress-targz": "^4.1.1", + "decompress-unzip": "^4.0.1", "ejs": "^3.1.10", "find-cache-dir": "^5.0.0", "isbinaryfile": "^5.0.2", @@ -24,6 +27,7 @@ "devDependencies": { "@biomejs/biome": "^1.7.2", "@types/bun": "^1.1.1", + "@types/decompress": "^4.2.7", "@types/ejs": "^3.1.5", "@types/prompts": "^2.4.9", "@types/validate-npm-package-name": "^4.0.2", @@ -32,7 +36,7 @@ "mock-stdin": "^1.0.0", "read-chunk": "^4.0.3", "typescript": "^5.4.5", - "zero-configuration": "^0.7.0" + "zero-configuration": "^0.7.1" }, "trustedDependencies": [ "zero-configuration" @@ -47,7 +51,6 @@ }, "bin": "./cli.ts", "files": [ - "utility", "*.ts" ], "keywords": [ @@ -67,7 +70,14 @@ ".create-now-temporary" ], "license": "MIT", - "biome": "recommended", + "biome": { + "extends": "recommended", + "files": { + "ignore": [ + "test/fixture" + ] + } + }, "vscode": "biome", "typescript": { "extends": "plugin", diff --git a/utility/prompt.ts b/prompt.ts similarity index 96% rename from utility/prompt.ts rename to prompt.ts index 455e67b..e16c064 100644 --- a/utility/prompt.ts +++ b/prompt.ts @@ -1,6 +1,6 @@ import prompts from 'prompts' -import type { Config } from '../types' import { log } from './log' +import type { Config } from './types' export const promptDirectories = async (directories: string[]) => { const response = await prompts({ diff --git a/utility/template-directory.ts b/template-directory.ts similarity index 75% rename from utility/template-directory.ts rename to template-directory.ts index 10a736d..46fc2ed 100644 --- a/utility/template-directory.ts +++ b/template-directory.ts @@ -1,5 +1,5 @@ -import { existsSync, readdirSync } from 'node:fs' -import { join } from 'node:path' +import { existsSync, lstatSync, readdirSync } from 'node:fs' +import { basename, join } from 'node:path' import { log } from './log' import { promptDirectories } from './prompt' @@ -38,8 +38,28 @@ const selectDirectory = (singleTemplate: boolean, directories: string[], templat return promptDirectories(directories) } +function findFirstFolder(folder: string) { + if (!folder) { + return '' + } + const files = readdirSync(folder) + let result = '' + + for (const file of files) { + const filePath = join(folder, file) + + if (lstatSync(filePath).isDirectory()) { + result = basename(filePath) + break + } + } + + return result +} + export const getTemplateDirectory = async (template = '', cachePath = '', pathOverride?: string) => { - const templatesPath = pathOverride || join(cachePath, 'template') + const containingFolder = findFirstFolder(cachePath) + const templatesPath = pathOverride || join(cachePath, containingFolder, 'template') if (!existsSync(templatesPath)) { log('Repository has no /template folder', 'error') diff --git a/test/basic.test.ts b/test/basic.test.ts index 503e28f..51e86e0 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -1,6 +1,6 @@ import { expect, spyOn, test } from 'bun:test' import { cachePath } from '../config' -import { loadPackage } from '../utility/load-package' +import { loadPackage } from '../load-package' test('Returns correct url for various packages.', async () => { let url = await loadPackage('padua') diff --git a/test/helper.test.ts b/test/helper.test.ts index d6c63dd..1e730be 100644 --- a/test/helper.test.ts +++ b/test/helper.test.ts @@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readdirSync, rmSync } from 'node:fs' import { join } from 'node:path' import { writeFile } from 'jest-fixture' import { type MockSTDIN, stdin } from 'mock-stdin' -import { getDestinationPath, validatePackageName } from '../utility/helper' +import { getDestinationPath, validatePackageName } from '../helper' let io: MockSTDIN beforeAll(() => { @@ -82,12 +82,12 @@ test('Clears non-empty destination path on confirmed prompt.', async () => { expect(mockExit.mock.calls.length).toBe(0) // Mock confirm. - const sendKeystrokes = async () => { + const sendKeystrokes = () => { io.send('y') io.send(keys.enter) } - setTimeout(() => sendKeystrokes().then(), 5) + setTimeout(() => sendKeystrokes(), 5) expect(await getDestinationPath('test/fixture/helper/non-empty')).toEqual(join(cwd, 'non-empty')) // Actual prompt. expect(existsSync(join(cwd, 'non-empty'))).toBeTruthy() @@ -119,12 +119,12 @@ test('Existing git repository is kept when overriding a folder.', async () => { expect(mockExit.mock.calls.length).toBe(0) // Mock confirm. - const sendKeystrokes = async () => { + const sendKeystrokes = () => { io.send('n') io.send(keys.enter) } - setTimeout(() => sendKeystrokes().then(), 5) + setTimeout(() => sendKeystrokes(), 5) expect(await getDestinationPath('test/fixture/helper/non-empty-git')).toEqual(join(cwd, 'non-empty-git')) // Actual prompt. diff --git a/test/install.test.ts b/test/install.test.ts index 5d59e8e..f27391b 100644 --- a/test/install.test.ts +++ b/test/install.test.ts @@ -1,10 +1,10 @@ import { expect, test } from 'bun:test' import { existsSync, rmSync } from 'node:fs' import { join } from 'node:path' -import { getConfig } from '../utility/get-config' -import { installDependencies } from '../utility/install-dependencies' -import { getTemplateDirectory } from '../utility/template-directory' -import { writeFiles } from '../utility/write-files' +import { getConfig } from '../get-config' +import { installDependencies } from '../install-dependencies' +import { getTemplateDirectory } from '../template-directory' +import { writeFiles } from '../write-files' test('Dependencies are installed if there are any.', async () => { const destination = join(process.cwd(), '.jest-temp-dependencies') diff --git a/test/template.test.ts b/test/template.test.ts index 9cab559..ead86d6 100644 --- a/test/template.test.ts +++ b/test/template.test.ts @@ -3,9 +3,9 @@ import { existsSync } from 'node:fs' import { join } from 'node:path' import { type MockSTDIN, stdin } from 'mock-stdin' import { cachePath } from '../config' -import { downloadTemplate } from '../utility/download-template' -import { cleanup } from '../utility/helper' -import { getTemplateDirectory } from '../utility/template-directory' +import { downloadTemplate } from '../download-template' +import { cleanup } from '../helper' +import { getTemplateDirectory } from '../template-directory' let io: MockSTDIN beforeAll(() => { @@ -26,9 +26,9 @@ test('Successfully downloads the template and stores it temporarly.', async () = await downloadTemplate('tobua/padua', cache) expect(existsSync(cache)).toBeTruthy() // Template directory exists. - expect(existsSync(join(cache, 'template'))).toBeTruthy() + expect(existsSync(join(cache, 'padua-main', 'template'))).toBeTruthy() // Whole repo is checked out. - expect(existsSync(join(cache, 'package.json'))).toBeTruthy() + expect(existsSync(join(cache, 'padua-main', 'package.json'))).toBeTruthy() cleanup(cache) @@ -48,13 +48,13 @@ test('Locates template in main directory.', async () => { test('Recoginizes several templates are available.', async () => { // Mocks selection of second template available. - const sendKeystrokes = async () => { + const sendKeystrokes = () => { io.send(keys.down) io.send(keys.down) io.send(keys.enter) } - setTimeout(() => sendKeystrokes().then(), 5) + setTimeout(() => sendKeystrokes(), 5) const templatePath = await getTemplateDirectory(undefined, undefined, join(process.cwd(), 'test/fixture/nested')) @@ -91,10 +91,10 @@ test('Successfully downloads template from main branch repository.', async () => await downloadTemplate('tobua/squak', cache) expect(existsSync(cache)).toBeTruthy() // Template directory exists. - expect(existsSync(join(cache, 'template'))).toBeTruthy() - expect(existsSync(join(cache, 'template/full/app.ts'))).toBeTruthy() + expect(existsSync(join(cache, 'squak-main', 'template'))).toBeTruthy() + expect(existsSync(join(cache, 'squak-main', 'template/full/app.ts'))).toBeTruthy() // Whole repo is checked out. - expect(existsSync(join(cache, 'package.json'))).toBeTruthy() + expect(existsSync(join(cache, 'squak-main', 'package.json'))).toBeTruthy() }) test('Finds template even when README present in template folder.', async () => { @@ -102,7 +102,7 @@ test('Finds template even when README present in template folder.', async () => // Uses template downloaded in previous test. const templateDirectory = await getTemplateDirectory('full', cache) - expect(templateDirectory).toEqual(join(cache, 'template/full')) + expect(templateDirectory).toEqual(join(cache, 'squak-main', 'template/full')) cleanup(cache) }) @@ -125,11 +125,11 @@ test('Downloads multiple templates in parallel.', async () => { expect(existsSync(cachePadua)).toBeTruthy() expect(existsSync(cachePapua)).toBeTruthy() // Template directory exists. - expect(existsSync(join(cachePadua, 'template'))).toBeTruthy() - expect(existsSync(join(cachePapua, 'template'))).toBeTruthy() + expect(existsSync(join(cachePadua, 'padua-main', 'template'))).toBeTruthy() + expect(existsSync(join(cachePapua, 'papua-main', 'template'))).toBeTruthy() // Whole repo is checked out. - expect(existsSync(join(cachePadua, 'package.json'))).toBeTruthy() - expect(existsSync(join(cachePapua, 'package.json'))).toBeTruthy() + expect(existsSync(join(cachePadua, 'padua-main', 'package.json'))).toBeTruthy() + expect(existsSync(join(cachePapua, 'papua-main', 'package.json'))).toBeTruthy() cleanup(cachePadua) cleanup(cachePapua) diff --git a/test/variables.test.ts b/test/variables.test.ts index 21939ab..459b15b 100644 --- a/test/variables.test.ts +++ b/test/variables.test.ts @@ -4,10 +4,10 @@ import { join } from 'node:path' import isPng from 'is-png' import { type MockSTDIN, stdin } from 'mock-stdin' import { readChunkSync } from 'read-chunk' -import { collectVariables } from '../utility/collect-variables' -import { getConfig } from '../utility/get-config' -import { getTemplateDirectory } from '../utility/template-directory' -import { writeFiles } from '../utility/write-files' +import { collectVariables } from '../collect-variables' +import { getConfig } from '../get-config' +import { getTemplateDirectory } from '../template-directory' +import { writeFiles } from '../write-files' let io: MockSTDIN beforeAll(() => { @@ -58,18 +58,18 @@ test('Static variables from template.json are written.', async () => { test('Dynamic variables from template.json are prompted and written.', async () => { // Mocks entering of dynamic variables. - const sendKeystrokesFirstVariable = async () => { + const sendKeystrokesFirstVariable = () => { io.send('first', 'ascii') io.send(keys.enter) } - const sendKeystrokesSecondVariable = async () => { + const sendKeystrokesSecondVariable = () => { io.send('second', 'ascii') io.send(keys.enter) } - setTimeout(() => sendKeystrokesFirstVariable().then(), 5) - setTimeout(() => sendKeystrokesSecondVariable().then(), 10) + setTimeout(() => sendKeystrokesFirstVariable(), 5) + setTimeout(() => sendKeystrokesSecondVariable(), 10) const templateDirectory = await getTemplateDirectory('dynamic', undefined, join(process.cwd(), 'test/fixture/variable')) const config = getConfig(templateDirectory) @@ -86,18 +86,18 @@ test('Dynamic variables from template.json are prompted and written.', async () test('Nested files are written as well and static, dynamic variables can be combined.', async () => { // Mocks entering of dynamic variables. - const sendKeystrokesFirstVariable = async () => { + const sendKeystrokesFirstVariable = () => { io.send('first', 'ascii') io.send(keys.enter) } - const sendKeystrokesSecondVariable = async () => { + const sendKeystrokesSecondVariable = () => { io.send('second', 'ascii') io.send(keys.enter) } - setTimeout(() => sendKeystrokesFirstVariable().then(), 5) - setTimeout(() => sendKeystrokesSecondVariable().then(), 10) + setTimeout(() => sendKeystrokesFirstVariable(), 5) + setTimeout(() => sendKeystrokesSecondVariable(), 10) const templateDirectory = await getTemplateDirectory('nested', undefined, join(process.cwd(), 'test/fixture/variable')) const config = getConfig(templateDirectory) diff --git a/utility/download-template.ts b/utility/download-template.ts deleted file mode 100644 index e3573b0..0000000 --- a/utility/download-template.ts +++ /dev/null @@ -1,22 +0,0 @@ -// @ts-ignore -import download from 'download-git-repo' -import { log } from './log' - -const attemptDownload = (url: string, cachePath: string, callback: (error: string) => void) => download(url, cachePath, callback) - -export const downloadTemplate = async (url: string, cachePath: string) => - new Promise((done) => { - attemptDownload(url, cachePath, (error) => { - if (error) { - // Plugin defaults to master branch, on error reattempt with main branch. - attemptDownload(`${url}#main`, cachePath, (secondError) => { - if (secondError) { - log(`Couldn't download repository from ${url}`, 'error') - } - done() - }) - } else { - done() - } - }) - }) diff --git a/utility/write-files.ts b/write-files.ts similarity index 98% rename from utility/write-files.ts rename to write-files.ts index 602b3d0..3e84603 100644 --- a/utility/write-files.ts +++ b/write-files.ts @@ -2,8 +2,8 @@ import { copyFileSync, existsSync, mkdirSync, readdirSync, writeFileSync } from import { join } from 'node:path' import ejs from 'ejs' import { isBinaryFileSync } from 'isbinaryfile' -import type { Config } from '../types' import { log } from './log' +import type { Config } from './types' const writeDirectoryFiles = ( directory: string,