giget 0.1.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 - UnJS
3
+ Copyright (c) Pooya Parsa <pooya@pi0.io>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -89,7 +89,7 @@ A custom registry should provide an endpoint with dynamic path `/:template.json`
89
89
  - `tar` (required) Link to the tar download link.
90
90
  - `defaultDir`: (optional) Default cloning directory.
91
91
  - `url`: (optional) Webpage of the template.
92
- - `subpath`: (optional) Subpath inside the tar file.
92
+ - `subdir`: (optional) Directory inside the tar file.
93
93
  - `headers`: (optional) Custom headers to send while downloading template.
94
94
 
95
95
  Because of the simplicity, you can even use a github repository as template registry but also you can build something more powerful by bringing your own API.
@@ -135,7 +135,7 @@ const { source, dir } = await downloadTemplate('github:unjs/template')
135
135
  - `provider`: (string) Either `github`, `gitlab`, `bitbucket` or `sourcehut`. The default is `github`.
136
136
  - `repo`: (string) Name of repository in format of `{username}/{reponame}`.
137
137
  - `ref`: (string) Git ref (branch or commit or tag). The default value is `main`.
138
- - `subdirpath`: (string) subdir of the repo to clone from. The default value is none.
138
+ - `subdir`: (string) Directory of the repo to clone from. The default value is none.
139
139
  - `force`: (boolean) Extract to the exisiting dir even if already exsists.
140
140
  - `forceClean`: (boolean) ⚠️ Clean ups any existing directory or file before cloning.
141
141
  - `offline`: (boolean) Do not attempt to download and use cached version.
package/dist/cli.cjs CHANGED
@@ -4,52 +4,50 @@
4
4
  const node_path = require('node:path');
5
5
  const mri = require('mri');
6
6
  const colorette = require('colorette');
7
- const giget = require('./shared/giget.73314e21.cjs');
7
+ const giget = require('./shared/giget.1028e17a.cjs');
8
8
  require('node:fs/promises');
9
9
  require('node:os');
10
10
  require('node:fs');
11
11
  require('tar');
12
12
  require('pathe');
13
13
  require('defu');
14
- require('node:stream/promises');
14
+ require('node:stream');
15
15
  require('node:child_process');
16
+ require('node:util');
16
17
  require('node-fetch-native');
17
-
18
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
19
-
20
- const mri__default = /*#__PURE__*/_interopDefaultLegacy(mri);
18
+ require('https-proxy-agent');
21
19
 
22
20
  async function main() {
23
- const args = mri__default(process.argv.slice(2), {
21
+ const arguments_ = mri(process.argv.slice(2), {
24
22
  boolean: ["help", "force", "force-clean", "offline", "prefer-offline", "shell", "verbose"],
25
23
  string: ["registry", "cwd", "auth"]
26
24
  });
27
- const input = args._[0];
28
- const dir = args._[1];
29
- if (!input || args.help || args.h) {
25
+ const input = arguments_._[0];
26
+ const dir = arguments_._[1];
27
+ if (!input || arguments_.help || arguments_.h) {
30
28
  console.error("Usage: npx getgit@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]");
31
29
  process.exit(1);
32
30
  }
33
- if (args.verbose) {
31
+ if (arguments_.verbose) {
34
32
  process.env.DEBUG = process.env.DEBUG || "true";
35
33
  }
36
34
  const r = await giget.downloadTemplate(input, {
37
35
  dir,
38
- force: args.force,
39
- forceClean: args["force-clean"],
40
- offline: args.offline,
41
- registry: args.registry,
42
- cwd: args.cwd,
43
- auth: args.auth
36
+ force: arguments_.force,
37
+ forceClean: arguments_["force-clean"],
38
+ offline: arguments_.offline,
39
+ registry: arguments_.registry,
40
+ cwd: arguments_.cwd,
41
+ auth: arguments_.auth
44
42
  });
45
43
  console.log(`\u2728 Successfully cloned ${colorette.cyan(r.name || r.url)} to ${colorette.cyan(node_path.relative(process.cwd(), r.dir))}
46
44
  `);
47
- if (args.shell) {
45
+ if (arguments_.shell) {
48
46
  giget.startShell(r.dir);
49
47
  }
50
48
  process.exit(0);
51
49
  }
52
- main().catch((err) => {
53
- console.error(err);
50
+ main().catch((error) => {
51
+ console.error(error);
54
52
  process.exit(1);
55
53
  });
package/dist/cli.mjs CHANGED
@@ -2,48 +2,50 @@
2
2
  import { relative } from 'node:path';
3
3
  import mri from 'mri';
4
4
  import { cyan } from 'colorette';
5
- import { d as downloadTemplate, s as startShell } from './shared/giget.ae887001.mjs';
5
+ import { d as downloadTemplate, s as startShell } from './shared/giget.5e7ec864.mjs';
6
6
  import 'node:fs/promises';
7
7
  import 'node:os';
8
8
  import 'node:fs';
9
9
  import 'tar';
10
10
  import 'pathe';
11
11
  import 'defu';
12
- import 'node:stream/promises';
12
+ import 'node:stream';
13
13
  import 'node:child_process';
14
+ import 'node:util';
14
15
  import 'node-fetch-native';
16
+ import 'https-proxy-agent';
15
17
 
16
18
  async function main() {
17
- const args = mri(process.argv.slice(2), {
19
+ const arguments_ = mri(process.argv.slice(2), {
18
20
  boolean: ["help", "force", "force-clean", "offline", "prefer-offline", "shell", "verbose"],
19
21
  string: ["registry", "cwd", "auth"]
20
22
  });
21
- const input = args._[0];
22
- const dir = args._[1];
23
- if (!input || args.help || args.h) {
23
+ const input = arguments_._[0];
24
+ const dir = arguments_._[1];
25
+ if (!input || arguments_.help || arguments_.h) {
24
26
  console.error("Usage: npx getgit@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]");
25
27
  process.exit(1);
26
28
  }
27
- if (args.verbose) {
29
+ if (arguments_.verbose) {
28
30
  process.env.DEBUG = process.env.DEBUG || "true";
29
31
  }
30
32
  const r = await downloadTemplate(input, {
31
33
  dir,
32
- force: args.force,
33
- forceClean: args["force-clean"],
34
- offline: args.offline,
35
- registry: args.registry,
36
- cwd: args.cwd,
37
- auth: args.auth
34
+ force: arguments_.force,
35
+ forceClean: arguments_["force-clean"],
36
+ offline: arguments_.offline,
37
+ registry: arguments_.registry,
38
+ cwd: arguments_.cwd,
39
+ auth: arguments_.auth
38
40
  });
39
41
  console.log(`\u2728 Successfully cloned ${cyan(r.name || r.url)} to ${cyan(relative(process.cwd(), r.dir))}
40
42
  `);
41
- if (args.shell) {
43
+ if (arguments_.shell) {
42
44
  startShell(r.dir);
43
45
  }
44
46
  process.exit(0);
45
47
  }
46
- main().catch((err) => {
47
- console.error(err);
48
+ main().catch((error) => {
49
+ console.error(error);
48
50
  process.exit(1);
49
51
  });
package/dist/index.cjs CHANGED
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const giget = require('./shared/giget.73314e21.cjs');
3
+ const giget = require('./shared/giget.1028e17a.cjs');
6
4
  require('node:fs/promises');
7
5
  require('node:os');
8
6
  require('node:fs');
9
7
  require('tar');
10
8
  require('pathe');
11
9
  require('defu');
12
- require('node:stream/promises');
10
+ require('node:stream');
13
11
  require('node:child_process');
12
+ require('node:util');
14
13
  require('node-fetch-native');
14
+ require('https-proxy-agent');
15
15
 
16
16
 
17
17
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  interface GitInfo {
2
- provider: 'github' | 'gitlab' | 'bitbucket' | 'sourcehut';
2
+ provider: "github" | "gitlab" | "bitbucket" | "sourcehut";
3
3
  repo: string;
4
4
  subdir: string;
5
5
  ref: string;
@@ -32,11 +32,11 @@ interface DownloadTemplateOptions {
32
32
  cwd?: string;
33
33
  auth?: string;
34
34
  }
35
- declare type DownloadTemplateResult = Omit<TemplateInfo, 'dir' | 'source'> & {
35
+ declare type DownloadTemplateResult = Omit<TemplateInfo, "dir" | "source"> & {
36
36
  dir: string;
37
37
  source: string;
38
38
  };
39
- declare function downloadTemplate(input: string, opts?: DownloadTemplateOptions): Promise<DownloadTemplateResult>;
39
+ declare function downloadTemplate(input: string, options?: DownloadTemplateOptions): Promise<DownloadTemplateResult>;
40
40
 
41
41
  declare const registryProvider: (registryEndpoint?: string) => TemplateProvider;
42
42
 
package/dist/index.mjs CHANGED
@@ -1,10 +1,12 @@
1
- export { d as downloadTemplate, r as registryProvider, s as startShell } from './shared/giget.ae887001.mjs';
1
+ export { d as downloadTemplate, r as registryProvider, s as startShell } from './shared/giget.5e7ec864.mjs';
2
2
  import 'node:fs/promises';
3
3
  import 'node:os';
4
4
  import 'node:fs';
5
5
  import 'tar';
6
6
  import 'pathe';
7
7
  import 'defu';
8
- import 'node:stream/promises';
8
+ import 'node:stream';
9
9
  import 'node:child_process';
10
+ import 'node:util';
10
11
  import 'node-fetch-native';
12
+ import 'https-proxy-agent';
@@ -6,29 +6,27 @@ const node_fs = require('node:fs');
6
6
  const tar = require('tar');
7
7
  const pathe = require('pathe');
8
8
  const defu = require('defu');
9
- const promises$1 = require('node:stream/promises');
9
+ const node_stream = require('node:stream');
10
10
  const node_child_process = require('node:child_process');
11
- const fetch = require('node-fetch-native');
11
+ const node_util = require('node:util');
12
+ const nodeFetchNative = require('node-fetch-native');
13
+ const createHttpsProxyAgent = require('https-proxy-agent');
12
14
 
13
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
14
-
15
- const fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
16
-
17
- async function download(url, filePath, opts = {}) {
15
+ async function download(url, filePath, options = {}) {
18
16
  const infoPath = filePath + ".json";
19
17
  const info = JSON.parse(await promises.readFile(infoPath, "utf8").catch(() => "{}"));
20
- const headRes = await fetch.fetch(url, { method: "HEAD", headers: opts.headers }).catch(() => null);
21
- const etag = headRes?.headers.get("etag");
18
+ const headResponse = await sendFetch(url, { method: "HEAD", headers: options.headers }).catch(() => void 0);
19
+ const etag = headResponse?.headers.get("etag");
22
20
  if (info.etag === etag && node_fs.existsSync(filePath)) {
23
21
  return;
24
22
  }
25
23
  info.etag = etag;
26
- const res = await fetch.fetch(url, { headers: opts.headers });
27
- if (res.status >= 400) {
28
- throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
24
+ const response = await sendFetch(url, { headers: options.headers });
25
+ if (response.status >= 400) {
26
+ throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
29
27
  }
30
28
  const stream = node_fs.createWriteStream(filePath);
31
- await promises$1.pipeline(res.body, stream);
29
+ await node_util.promisify(node_stream.pipeline)(response.body, stream);
32
30
  await promises.writeFile(infoPath, JSON.stringify(info), "utf8");
33
31
  }
34
32
  const inputRegex = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w.-]+)?/;
@@ -37,14 +35,19 @@ function parseGitURI(input) {
37
35
  return {
38
36
  repo: m.repo,
39
37
  subdir: m.subdir || "/",
40
- ref: m.ref ? m.ref.substring(1) : "main"
38
+ ref: m.ref ? m.ref.slice(1) : "main"
41
39
  };
42
40
  }
43
- function debug(...args) {
41
+ function debug(...arguments_) {
44
42
  if (process.env.DEBUG) {
45
- console.debug("[giget]", ...args);
43
+ console.debug("[giget]", ...arguments_);
46
44
  }
47
45
  }
46
+ async function sendFetch(url, options) {
47
+ const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
48
+ const requestOptions = proxy ? { agent: createHttpsProxyAgent(proxy), ...options } : options;
49
+ return await nodeFetchNative.fetch(url, requestOptions);
50
+ }
48
51
  function currentShell() {
49
52
  if (process.env.SHELL) {
50
53
  return process.env.SHELL;
@@ -65,46 +68,46 @@ function startShell(cwd) {
65
68
  });
66
69
  }
67
70
 
68
- const github = (input, opts) => {
71
+ const github = (input, options) => {
69
72
  const parsed = parseGitURI(input);
70
73
  return {
71
74
  name: parsed.repo.replace("/", "-"),
72
75
  version: parsed.ref,
73
76
  subdir: parsed.subdir,
74
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
77
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
75
78
  url: `https://github.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
76
79
  tar: `https://github.com/${parsed.repo}/archive/${parsed.ref}.tar.gz`
77
80
  };
78
81
  };
79
- const gitlab = (input, opts) => {
82
+ const gitlab = (input, options) => {
80
83
  const parsed = parseGitURI(input);
81
84
  return {
82
85
  name: parsed.repo.replace("/", "-"),
83
86
  version: parsed.ref,
84
87
  subdir: parsed.subdir,
85
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
88
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
86
89
  url: `https://gitlab.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
87
90
  tar: `https://gitlab.com/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
88
91
  };
89
92
  };
90
- const bitbucket = (input, opts) => {
93
+ const bitbucket = (input, options) => {
91
94
  const parsed = parseGitURI(input);
92
95
  return {
93
96
  name: parsed.repo.replace("/", "-"),
94
97
  version: parsed.ref,
95
98
  subdir: parsed.subdir,
96
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
99
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
97
100
  url: `https://bitbucket.com/${parsed.repo}/src/${parsed.ref}${parsed.subdir}`,
98
101
  tar: `https://bitbucket.org/${parsed.repo}/get/${parsed.ref}.tar.gz`
99
102
  };
100
103
  };
101
- const sourcehut = (input, opts) => {
104
+ const sourcehut = (input, options) => {
102
105
  const parsed = parseGitURI(input);
103
106
  return {
104
107
  name: parsed.repo.replace("/", "-"),
105
108
  version: parsed.ref,
106
109
  subdir: parsed.subdir,
107
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
110
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
108
111
  url: `https://git.sr.ht/~${parsed.repo}/tree/${parsed.ref}/item${parsed.subdir}`,
109
112
  tar: `https://git.sr.ht/~${parsed.repo}/archive/${parsed.ref}.tar.gz`
110
113
  };
@@ -122,11 +125,11 @@ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
122
125
  return async (input) => {
123
126
  const start = Date.now();
124
127
  const registryURL = `${registryEndpoint}/${input}.json`;
125
- const res = await fetch__default(registryURL);
126
- if (res.status >= 400) {
127
- throw new Error(`Failed to download ${input} template info from ${registryURL}: ${res.status} ${res.statusText}`);
128
+ const result = await sendFetch(registryURL);
129
+ if (result.status >= 400) {
130
+ throw new Error(`Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`);
128
131
  }
129
- const info = await res.json();
132
+ const info = await result.json();
130
133
  if (!info.tar || !info.name) {
131
134
  throw new Error(`Invalid template info from ${registryURL}. name or tar fields are missing!`);
132
135
  }
@@ -136,56 +139,56 @@ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
136
139
  };
137
140
 
138
141
  const sourceProtoRe = /^([\w-.]+):/;
139
- async function downloadTemplate(input, opts = {}) {
140
- opts = defu.defu({
142
+ async function downloadTemplate(input, options = {}) {
143
+ options = defu.defu({
141
144
  registry: process.env.GIGET_REGISTRY,
142
145
  auth: process.env.GIGET_AUTH
143
- }, opts);
144
- const registry = opts.registry !== false ? registryProvider(opts.registry) : null;
145
- let providerName = opts.provider || (registryProvider ? "registry" : "github");
146
+ }, options);
147
+ const registry = options.registry !== false ? registryProvider(options.registry) : void 0;
148
+ let providerName = options.provider || (registryProvider ? "registry" : "github");
146
149
  let source = input;
147
150
  const sourceProvierMatch = input.match(sourceProtoRe);
148
151
  if (sourceProvierMatch) {
149
152
  providerName = sourceProvierMatch[1];
150
- source = input.substring(sourceProvierMatch[0].length);
153
+ source = input.slice(sourceProvierMatch[0].length);
151
154
  }
152
- const provider = opts.providers?.[providerName] || providers[providerName] || registry;
155
+ const provider = options.providers?.[providerName] || providers[providerName] || registry;
153
156
  if (!provider) {
154
157
  throw new Error(`Unsupported provider: ${providerName}`);
155
158
  }
156
- const template = await Promise.resolve().then(() => provider(source, { auth: opts.auth })).catch((err) => {
157
- throw new Error(`Failed to download template from ${providerName}: ${err.message}`);
159
+ const template = await Promise.resolve().then(() => provider(source, { auth: options.auth })).catch((error) => {
160
+ throw new Error(`Failed to download template from ${providerName}: ${error.message}`);
158
161
  });
159
- template.name = (template.name || "template").replace(/[^a-z0-9-]/gi, "-");
160
- template.defaultDir = (template.defaultDir || template.name).replace(/[^a-z0-9-]/gi, "-");
161
- const cwd = pathe.resolve(opts.cwd || ".");
162
- const extractPath = pathe.resolve(cwd, opts.dir || template.defaultDir);
163
- if (opts.forceClean) {
162
+ template.name = (template.name || "template").replace(/[^\da-z-]/gi, "-");
163
+ template.defaultDir = (template.defaultDir || template.name).replace(/[^\da-z-]/gi, "-");
164
+ const cwd = pathe.resolve(options.cwd || ".");
165
+ const extractPath = pathe.resolve(cwd, options.dir || template.defaultDir);
166
+ if (options.forceClean) {
164
167
  await promises.rm(extractPath, { recursive: true, force: true });
165
168
  }
166
- if (!opts.force && node_fs.existsSync(extractPath) && node_fs.readdirSync(extractPath).length) {
169
+ if (!options.force && node_fs.existsSync(extractPath) && node_fs.readdirSync(extractPath).length > 0) {
167
170
  throw new Error(`Destination ${extractPath} already exists.`);
168
171
  }
169
172
  await promises.mkdir(extractPath, { recursive: true });
170
- const tmpDir = pathe.resolve(node_os.homedir(), ".giget", opts.provider, template.name);
171
- const tarPath = pathe.resolve(tmpDir, (template.version || template.name) + ".tar.gz");
172
- if (opts.preferOffline && node_fs.existsSync(tarPath)) {
173
- opts.offline = true;
173
+ const temporaryDirectory = pathe.resolve(node_os.homedir(), ".giget", options.provider, template.name);
174
+ const tarPath = pathe.resolve(temporaryDirectory, (template.version || template.name) + ".tar.gz");
175
+ if (options.preferOffline && node_fs.existsSync(tarPath)) {
176
+ options.offline = true;
174
177
  }
175
- if (!opts.offline) {
178
+ if (!options.offline) {
176
179
  await promises.mkdir(pathe.dirname(tarPath), { recursive: true });
177
180
  const s2 = Date.now();
178
- await download(template.tar, tarPath, { headers: template.headers }).catch((err) => {
181
+ await download(template.tar, tarPath, { headers: template.headers }).catch((error) => {
179
182
  if (!node_fs.existsSync(tarPath)) {
180
- throw err;
183
+ throw error;
181
184
  }
182
- debug("Download error. Using cached version:", err);
183
- opts.offline = true;
185
+ debug("Download error. Using cached version:", error);
186
+ options.offline = true;
184
187
  });
185
188
  debug(`Downloaded ${template.tar} to ${tarPath} in ${Date.now() - s2}ms`);
186
189
  }
187
190
  if (!node_fs.existsSync(tarPath)) {
188
- throw new Error(`Tarball not found: ${tarPath} (offline: ${opts.offline})`);
191
+ throw new Error(`Tarball not found: ${tarPath} (offline: ${options.offline})`);
189
192
  }
190
193
  const s = Date.now();
191
194
  const subdir = template.subdir?.replace(/^\//, "") || "";
@@ -196,7 +199,7 @@ async function downloadTemplate(input, opts = {}) {
196
199
  entry.path = entry.path.split("/").splice(1).join("/");
197
200
  if (subdir) {
198
201
  if (entry.path.startsWith(subdir + "/")) {
199
- entry.path = entry.path.substring(subdir.length);
202
+ entry.path = entry.path.slice(subdir.length);
200
203
  } else {
201
204
  entry.path = "";
202
205
  }
@@ -4,25 +4,27 @@ import { existsSync, createWriteStream, readdirSync } from 'node:fs';
4
4
  import { extract } from 'tar';
5
5
  import { resolve, relative, dirname } from 'pathe';
6
6
  import { defu } from 'defu';
7
- import { pipeline } from 'node:stream/promises';
7
+ import { pipeline } from 'node:stream';
8
8
  import { spawnSync } from 'node:child_process';
9
- import fetch$1, { fetch } from 'node-fetch-native';
9
+ import { promisify } from 'node:util';
10
+ import { fetch } from 'node-fetch-native';
11
+ import createHttpsProxyAgent from 'https-proxy-agent';
10
12
 
11
- async function download(url, filePath, opts = {}) {
13
+ async function download(url, filePath, options = {}) {
12
14
  const infoPath = filePath + ".json";
13
15
  const info = JSON.parse(await readFile(infoPath, "utf8").catch(() => "{}"));
14
- const headRes = await fetch(url, { method: "HEAD", headers: opts.headers }).catch(() => null);
15
- const etag = headRes?.headers.get("etag");
16
+ const headResponse = await sendFetch(url, { method: "HEAD", headers: options.headers }).catch(() => void 0);
17
+ const etag = headResponse?.headers.get("etag");
16
18
  if (info.etag === etag && existsSync(filePath)) {
17
19
  return;
18
20
  }
19
21
  info.etag = etag;
20
- const res = await fetch(url, { headers: opts.headers });
21
- if (res.status >= 400) {
22
- throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
22
+ const response = await sendFetch(url, { headers: options.headers });
23
+ if (response.status >= 400) {
24
+ throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
23
25
  }
24
26
  const stream = createWriteStream(filePath);
25
- await pipeline(res.body, stream);
27
+ await promisify(pipeline)(response.body, stream);
26
28
  await writeFile(infoPath, JSON.stringify(info), "utf8");
27
29
  }
28
30
  const inputRegex = /^(?<repo>[\w.-]+\/[\w.-]+)(?<subdir>[^#]+)?(?<ref>#[\w.-]+)?/;
@@ -31,14 +33,19 @@ function parseGitURI(input) {
31
33
  return {
32
34
  repo: m.repo,
33
35
  subdir: m.subdir || "/",
34
- ref: m.ref ? m.ref.substring(1) : "main"
36
+ ref: m.ref ? m.ref.slice(1) : "main"
35
37
  };
36
38
  }
37
- function debug(...args) {
39
+ function debug(...arguments_) {
38
40
  if (process.env.DEBUG) {
39
- console.debug("[giget]", ...args);
41
+ console.debug("[giget]", ...arguments_);
40
42
  }
41
43
  }
44
+ async function sendFetch(url, options) {
45
+ const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
46
+ const requestOptions = proxy ? { agent: createHttpsProxyAgent(proxy), ...options } : options;
47
+ return await fetch(url, requestOptions);
48
+ }
42
49
  function currentShell() {
43
50
  if (process.env.SHELL) {
44
51
  return process.env.SHELL;
@@ -59,46 +66,46 @@ function startShell(cwd) {
59
66
  });
60
67
  }
61
68
 
62
- const github = (input, opts) => {
69
+ const github = (input, options) => {
63
70
  const parsed = parseGitURI(input);
64
71
  return {
65
72
  name: parsed.repo.replace("/", "-"),
66
73
  version: parsed.ref,
67
74
  subdir: parsed.subdir,
68
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
75
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
69
76
  url: `https://github.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
70
77
  tar: `https://github.com/${parsed.repo}/archive/${parsed.ref}.tar.gz`
71
78
  };
72
79
  };
73
- const gitlab = (input, opts) => {
80
+ const gitlab = (input, options) => {
74
81
  const parsed = parseGitURI(input);
75
82
  return {
76
83
  name: parsed.repo.replace("/", "-"),
77
84
  version: parsed.ref,
78
85
  subdir: parsed.subdir,
79
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
86
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
80
87
  url: `https://gitlab.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
81
88
  tar: `https://gitlab.com/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
82
89
  };
83
90
  };
84
- const bitbucket = (input, opts) => {
91
+ const bitbucket = (input, options) => {
85
92
  const parsed = parseGitURI(input);
86
93
  return {
87
94
  name: parsed.repo.replace("/", "-"),
88
95
  version: parsed.ref,
89
96
  subdir: parsed.subdir,
90
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
97
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
91
98
  url: `https://bitbucket.com/${parsed.repo}/src/${parsed.ref}${parsed.subdir}`,
92
99
  tar: `https://bitbucket.org/${parsed.repo}/get/${parsed.ref}.tar.gz`
93
100
  };
94
101
  };
95
- const sourcehut = (input, opts) => {
102
+ const sourcehut = (input, options) => {
96
103
  const parsed = parseGitURI(input);
97
104
  return {
98
105
  name: parsed.repo.replace("/", "-"),
99
106
  version: parsed.ref,
100
107
  subdir: parsed.subdir,
101
- headers: { Authorization: opts.auth ? `Bearer ${opts.auth}` : void 0 },
108
+ headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
102
109
  url: `https://git.sr.ht/~${parsed.repo}/tree/${parsed.ref}/item${parsed.subdir}`,
103
110
  tar: `https://git.sr.ht/~${parsed.repo}/archive/${parsed.ref}.tar.gz`
104
111
  };
@@ -116,11 +123,11 @@ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
116
123
  return async (input) => {
117
124
  const start = Date.now();
118
125
  const registryURL = `${registryEndpoint}/${input}.json`;
119
- const res = await fetch$1(registryURL);
120
- if (res.status >= 400) {
121
- throw new Error(`Failed to download ${input} template info from ${registryURL}: ${res.status} ${res.statusText}`);
126
+ const result = await sendFetch(registryURL);
127
+ if (result.status >= 400) {
128
+ throw new Error(`Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`);
122
129
  }
123
- const info = await res.json();
130
+ const info = await result.json();
124
131
  if (!info.tar || !info.name) {
125
132
  throw new Error(`Invalid template info from ${registryURL}. name or tar fields are missing!`);
126
133
  }
@@ -130,56 +137,56 @@ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
130
137
  };
131
138
 
132
139
  const sourceProtoRe = /^([\w-.]+):/;
133
- async function downloadTemplate(input, opts = {}) {
134
- opts = defu({
140
+ async function downloadTemplate(input, options = {}) {
141
+ options = defu({
135
142
  registry: process.env.GIGET_REGISTRY,
136
143
  auth: process.env.GIGET_AUTH
137
- }, opts);
138
- const registry = opts.registry !== false ? registryProvider(opts.registry) : null;
139
- let providerName = opts.provider || (registryProvider ? "registry" : "github");
144
+ }, options);
145
+ const registry = options.registry !== false ? registryProvider(options.registry) : void 0;
146
+ let providerName = options.provider || (registryProvider ? "registry" : "github");
140
147
  let source = input;
141
148
  const sourceProvierMatch = input.match(sourceProtoRe);
142
149
  if (sourceProvierMatch) {
143
150
  providerName = sourceProvierMatch[1];
144
- source = input.substring(sourceProvierMatch[0].length);
151
+ source = input.slice(sourceProvierMatch[0].length);
145
152
  }
146
- const provider = opts.providers?.[providerName] || providers[providerName] || registry;
153
+ const provider = options.providers?.[providerName] || providers[providerName] || registry;
147
154
  if (!provider) {
148
155
  throw new Error(`Unsupported provider: ${providerName}`);
149
156
  }
150
- const template = await Promise.resolve().then(() => provider(source, { auth: opts.auth })).catch((err) => {
151
- throw new Error(`Failed to download template from ${providerName}: ${err.message}`);
157
+ const template = await Promise.resolve().then(() => provider(source, { auth: options.auth })).catch((error) => {
158
+ throw new Error(`Failed to download template from ${providerName}: ${error.message}`);
152
159
  });
153
- template.name = (template.name || "template").replace(/[^a-z0-9-]/gi, "-");
154
- template.defaultDir = (template.defaultDir || template.name).replace(/[^a-z0-9-]/gi, "-");
155
- const cwd = resolve(opts.cwd || ".");
156
- const extractPath = resolve(cwd, opts.dir || template.defaultDir);
157
- if (opts.forceClean) {
160
+ template.name = (template.name || "template").replace(/[^\da-z-]/gi, "-");
161
+ template.defaultDir = (template.defaultDir || template.name).replace(/[^\da-z-]/gi, "-");
162
+ const cwd = resolve(options.cwd || ".");
163
+ const extractPath = resolve(cwd, options.dir || template.defaultDir);
164
+ if (options.forceClean) {
158
165
  await rm(extractPath, { recursive: true, force: true });
159
166
  }
160
- if (!opts.force && existsSync(extractPath) && readdirSync(extractPath).length) {
167
+ if (!options.force && existsSync(extractPath) && readdirSync(extractPath).length > 0) {
161
168
  throw new Error(`Destination ${extractPath} already exists.`);
162
169
  }
163
170
  await mkdir(extractPath, { recursive: true });
164
- const tmpDir = resolve(homedir(), ".giget", opts.provider, template.name);
165
- const tarPath = resolve(tmpDir, (template.version || template.name) + ".tar.gz");
166
- if (opts.preferOffline && existsSync(tarPath)) {
167
- opts.offline = true;
171
+ const temporaryDirectory = resolve(homedir(), ".giget", options.provider, template.name);
172
+ const tarPath = resolve(temporaryDirectory, (template.version || template.name) + ".tar.gz");
173
+ if (options.preferOffline && existsSync(tarPath)) {
174
+ options.offline = true;
168
175
  }
169
- if (!opts.offline) {
176
+ if (!options.offline) {
170
177
  await mkdir(dirname(tarPath), { recursive: true });
171
178
  const s2 = Date.now();
172
- await download(template.tar, tarPath, { headers: template.headers }).catch((err) => {
179
+ await download(template.tar, tarPath, { headers: template.headers }).catch((error) => {
173
180
  if (!existsSync(tarPath)) {
174
- throw err;
181
+ throw error;
175
182
  }
176
- debug("Download error. Using cached version:", err);
177
- opts.offline = true;
183
+ debug("Download error. Using cached version:", error);
184
+ options.offline = true;
178
185
  });
179
186
  debug(`Downloaded ${template.tar} to ${tarPath} in ${Date.now() - s2}ms`);
180
187
  }
181
188
  if (!existsSync(tarPath)) {
182
- throw new Error(`Tarball not found: ${tarPath} (offline: ${opts.offline})`);
189
+ throw new Error(`Tarball not found: ${tarPath} (offline: ${options.offline})`);
183
190
  }
184
191
  const s = Date.now();
185
192
  const subdir = template.subdir?.replace(/^\//, "") || "";
@@ -190,7 +197,7 @@ async function downloadTemplate(input, opts = {}) {
190
197
  entry.path = entry.path.split("/").splice(1).join("/");
191
198
  if (subdir) {
192
199
  if (entry.path.startsWith(subdir + "/")) {
193
- entry.path = entry.path.substring(subdir.length);
200
+ entry.path = entry.path.slice(subdir.length);
194
201
  } else {
195
202
  entry.path = "";
196
203
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giget",
3
- "version": "0.1.6",
3
+ "version": "1.0.0",
4
4
  "description": "Download templates and git repositories with pleasure!",
5
5
  "repository": "unjs/giget",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "exports": {
13
13
  ".": {
14
14
  "import": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
15
16
  "require": "./dist/index.cjs"
16
17
  }
17
18
  },
@@ -21,34 +22,36 @@
21
22
  "files": [
22
23
  "dist"
23
24
  ],
24
- "dependencies": {
25
- "colorette": "^2.0.19",
26
- "defu": "^6.1.0",
27
- "mri": "^1.2.0",
28
- "node-fetch-native": "^0.1.4",
29
- "pathe": "^0.3.7",
30
- "tar": "^6.1.11"
31
- },
32
- "devDependencies": {
33
- "@nuxtjs/eslint-config-typescript": "^11.0.0",
34
- "@types/node": "^18.7.16",
35
- "@types/tar": "^6.1.2",
36
- "@vitest/coverage-c8": "^0.23.2",
37
- "eslint": "^8.23.1",
38
- "jiti": "^1.15.0",
39
- "standard-version": "^9.5.0",
40
- "typescript": "^4.8.3",
41
- "unbuild": "^0.8.11",
42
- "vitest": "^0.23.2"
43
- },
44
- "packageManager": "pnpm@7.11.0",
45
25
  "scripts": {
46
26
  "build": "unbuild",
47
27
  "dev": "vitest dev",
48
28
  "giget": "jiti ./src/cli.ts",
49
29
  "lint": "eslint --ext .ts,.js,.mjs,.cjs .",
30
+ "prepack": "unbuild",
50
31
  "play": "pnpm giget --force-clean --verbose unjs .tmp/clone",
51
32
  "release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
52
33
  "test": "pnpm lint && vitest run --coverage"
53
- }
54
- }
34
+ },
35
+ "dependencies": {
36
+ "colorette": "^2.0.19",
37
+ "defu": "^6.1.1",
38
+ "https-proxy-agent": "^5.0.1",
39
+ "mri": "^1.2.0",
40
+ "node-fetch-native": "^1.0.1",
41
+ "pathe": "^1.0.0",
42
+ "tar": "^6.1.12"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^18.11.9",
46
+ "@types/tar": "^6.1.3",
47
+ "@vitest/coverage-c8": "^0.25.2",
48
+ "eslint": "^8.27.0",
49
+ "eslint-config-unjs": "^0.0.2",
50
+ "jiti": "^1.16.0",
51
+ "standard-version": "^9.5.0",
52
+ "typescript": "^4.8.4",
53
+ "unbuild": "^0.9.4",
54
+ "vitest": "^0.25.2"
55
+ },
56
+ "packageManager": "pnpm@7.16.0"
57
+ }