giget 1.0.0 → 1.1.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/README.md CHANGED
@@ -36,7 +36,7 @@ npx giget@latest <template> [<dir>] [...options]
36
36
 
37
37
  ### Options
38
38
 
39
- - `--force`: Clone to exsiting directory even if exists.
39
+ - `--force`: Clone to existing directory even if exists.
40
40
  - `--offline`: Do not attempt to download and use cached version.
41
41
  - `--prefer-offline`: Use cache if exists otherwise try to download.
42
42
  - `--force-clean`: ⚠️ Remove any existing directory or file recusively before cloning.
@@ -92,7 +92,7 @@ A custom registry should provide an endpoint with dynamic path `/:template.json`
92
92
  - `subdir`: (optional) Directory inside the tar file.
93
93
  - `headers`: (optional) Custom headers to send while downloading template.
94
94
 
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.
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.
96
96
 
97
97
  ## Usage (Programmatic)
98
98
 
@@ -130,8 +130,8 @@ const { source, dir } = await downloadTemplate('github:unjs/template')
130
130
  **Options:**
131
131
 
132
132
  - `source`: (string) Input source in format of `[provider]:repo[/subpath][#ref]`.
133
- - `dir`: (string) Destination directory to clone to. If not provided, `user-name` will be used relative to the current directory.
134
133
  - `options`: (object) Options are usually inferred from the input string. You can customize them.
134
+ - `dir`: (string) Destination directory to clone to. If not provided, `user-name` will be used relative to the current directory.
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`.
@@ -188,7 +188,7 @@ const { source, dir } = await downloadRepo('themes:test', { providers: { themes
188
188
 
189
189
  ## Related projects
190
190
 
191
- Giget wouldn't be possible without inspering from former projects. In comparation giget does not depend on any local command which increases stability and performance, supports custom template providers, auth and many more features out of the box.
191
+ Giget wouldn't be possible without inspiration from former projects. In comparison, giget does not depend on any local command which increases stability and performance, supports custom template providers, auth and many more features out of the box.
192
192
 
193
193
  - https://github.com/samsonjs/gitter
194
194
  - https://github.com/tiged/tiged
package/dist/cli.cjs CHANGED
@@ -4,28 +4,38 @@
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.1028e17a.cjs');
7
+ const giget = require('./shared/giget.51477975.cjs');
8
8
  require('node:fs/promises');
9
- require('node:os');
10
9
  require('node:fs');
11
10
  require('tar');
12
11
  require('pathe');
13
12
  require('defu');
14
13
  require('node:stream');
15
14
  require('node:child_process');
15
+ require('node:os');
16
16
  require('node:util');
17
17
  require('node-fetch-native');
18
18
  require('https-proxy-agent');
19
19
 
20
20
  async function main() {
21
21
  const arguments_ = mri(process.argv.slice(2), {
22
- boolean: ["help", "force", "force-clean", "offline", "prefer-offline", "shell", "verbose"],
22
+ boolean: [
23
+ "help",
24
+ "force",
25
+ "force-clean",
26
+ "offline",
27
+ "prefer-offline",
28
+ "shell",
29
+ "verbose"
30
+ ],
23
31
  string: ["registry", "cwd", "auth"]
24
32
  });
25
33
  const input = arguments_._[0];
26
34
  const dir = arguments_._[1];
27
35
  if (!input || arguments_.help || arguments_.h) {
28
- console.error("Usage: npx getgit@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]");
36
+ console.error(
37
+ "Usage: npx giget@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]"
38
+ );
29
39
  process.exit(1);
30
40
  }
31
41
  if (arguments_.verbose) {
@@ -40,8 +50,12 @@ async function main() {
40
50
  cwd: arguments_.cwd,
41
51
  auth: arguments_.auth
42
52
  });
43
- console.log(`\u2728 Successfully cloned ${colorette.cyan(r.name || r.url)} to ${colorette.cyan(node_path.relative(process.cwd(), r.dir))}
44
- `);
53
+ console.log(
54
+ `\u2728 Successfully cloned ${colorette.cyan(r.name || r.url)} to ${colorette.cyan(
55
+ node_path.relative(process.cwd(), r.dir)
56
+ )}
57
+ `
58
+ );
45
59
  if (arguments_.shell) {
46
60
  giget.startShell(r.dir);
47
61
  }
package/dist/cli.d.ts CHANGED
@@ -1 +1,2 @@
1
1
 
2
+ export { }
package/dist/cli.mjs CHANGED
@@ -2,28 +2,38 @@
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.5e7ec864.mjs';
5
+ import { d as downloadTemplate, s as startShell } from './shared/giget.6c52cb03.mjs';
6
6
  import 'node:fs/promises';
7
- import 'node:os';
8
7
  import 'node:fs';
9
8
  import 'tar';
10
9
  import 'pathe';
11
10
  import 'defu';
12
11
  import 'node:stream';
13
12
  import 'node:child_process';
13
+ import 'node:os';
14
14
  import 'node:util';
15
15
  import 'node-fetch-native';
16
16
  import 'https-proxy-agent';
17
17
 
18
18
  async function main() {
19
19
  const arguments_ = mri(process.argv.slice(2), {
20
- boolean: ["help", "force", "force-clean", "offline", "prefer-offline", "shell", "verbose"],
20
+ boolean: [
21
+ "help",
22
+ "force",
23
+ "force-clean",
24
+ "offline",
25
+ "prefer-offline",
26
+ "shell",
27
+ "verbose"
28
+ ],
21
29
  string: ["registry", "cwd", "auth"]
22
30
  });
23
31
  const input = arguments_._[0];
24
32
  const dir = arguments_._[1];
25
33
  if (!input || arguments_.help || arguments_.h) {
26
- console.error("Usage: npx getgit@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]");
34
+ console.error(
35
+ "Usage: npx giget@latest <input> [<dir>] [--force] [--force-clean] [--offline] [--prefer-offline] [--shell] [--registry] [--no-registry] [--verbose] [--cwd] [--auth]"
36
+ );
27
37
  process.exit(1);
28
38
  }
29
39
  if (arguments_.verbose) {
@@ -38,8 +48,12 @@ async function main() {
38
48
  cwd: arguments_.cwd,
39
49
  auth: arguments_.auth
40
50
  });
41
- console.log(`\u2728 Successfully cloned ${cyan(r.name || r.url)} to ${cyan(relative(process.cwd(), r.dir))}
42
- `);
51
+ console.log(
52
+ `\u2728 Successfully cloned ${cyan(r.name || r.url)} to ${cyan(
53
+ relative(process.cwd(), r.dir)
54
+ )}
55
+ `
56
+ );
43
57
  if (arguments_.shell) {
44
58
  startShell(r.dir);
45
59
  }
package/dist/index.cjs CHANGED
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const giget = require('./shared/giget.1028e17a.cjs');
3
+ const giget = require('./shared/giget.51477975.cjs');
4
4
  require('node:fs/promises');
5
- require('node:os');
6
5
  require('node:fs');
7
6
  require('tar');
8
7
  require('pathe');
9
8
  require('defu');
10
9
  require('node:stream');
11
10
  require('node:child_process');
11
+ require('node:os');
12
12
  require('node:util');
13
13
  require('node-fetch-native');
14
14
  require('https-proxy-agent');
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ interface TemplateInfo {
16
16
  dir?: never;
17
17
  [key: string]: any;
18
18
  }
19
- declare type TemplateProvider = (input: string, options: {
19
+ type TemplateProvider = (input: string, options: {
20
20
  auth?: string;
21
21
  }) => TemplateInfo | Promise<TemplateInfo> | null;
22
22
 
@@ -32,13 +32,15 @@ interface DownloadTemplateOptions {
32
32
  cwd?: string;
33
33
  auth?: string;
34
34
  }
35
- declare type DownloadTemplateResult = Omit<TemplateInfo, "dir" | "source"> & {
35
+ type DownloadTemplateResult = Omit<TemplateInfo, "dir" | "source"> & {
36
36
  dir: string;
37
37
  source: string;
38
38
  };
39
39
  declare function downloadTemplate(input: string, options?: DownloadTemplateOptions): Promise<DownloadTemplateResult>;
40
40
 
41
- declare const registryProvider: (registryEndpoint?: string) => TemplateProvider;
41
+ declare const registryProvider: (registryEndpoint?: string, options?: {
42
+ auth?: string;
43
+ }) => TemplateProvider;
42
44
 
43
45
  declare function startShell(cwd: string): void;
44
46
 
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
- export { d as downloadTemplate, r as registryProvider, s as startShell } from './shared/giget.5e7ec864.mjs';
1
+ export { d as downloadTemplate, r as registryProvider, s as startShell } from './shared/giget.6c52cb03.mjs';
2
2
  import 'node:fs/promises';
3
- import 'node:os';
4
3
  import 'node:fs';
5
4
  import 'tar';
6
5
  import 'pathe';
7
6
  import 'defu';
8
7
  import 'node:stream';
9
8
  import 'node:child_process';
9
+ import 'node:os';
10
10
  import 'node:util';
11
11
  import 'node-fetch-native';
12
12
  import 'https-proxy-agent';
@@ -1,21 +1,26 @@
1
1
  'use strict';
2
2
 
3
3
  const promises = require('node:fs/promises');
4
- const node_os = require('node:os');
5
4
  const node_fs = require('node:fs');
6
5
  const tar = require('tar');
7
6
  const pathe = require('pathe');
8
7
  const defu = require('defu');
9
8
  const node_stream = require('node:stream');
10
9
  const node_child_process = require('node:child_process');
10
+ const node_os = require('node:os');
11
11
  const node_util = require('node:util');
12
12
  const nodeFetchNative = require('node-fetch-native');
13
13
  const createHttpsProxyAgent = require('https-proxy-agent');
14
14
 
15
15
  async function download(url, filePath, options = {}) {
16
16
  const infoPath = filePath + ".json";
17
- const info = JSON.parse(await promises.readFile(infoPath, "utf8").catch(() => "{}"));
18
- const headResponse = await sendFetch(url, { method: "HEAD", headers: options.headers }).catch(() => void 0);
17
+ const info = JSON.parse(
18
+ await promises.readFile(infoPath, "utf8").catch(() => "{}")
19
+ );
20
+ const headResponse = await sendFetch(url, {
21
+ method: "HEAD",
22
+ headers: options.headers
23
+ }).catch(() => void 0);
19
24
  const etag = headResponse?.headers.get("etag");
20
25
  if (info.etag === etag && node_fs.existsSync(filePath)) {
21
26
  return;
@@ -23,7 +28,9 @@ async function download(url, filePath, options = {}) {
23
28
  info.etag = etag;
24
29
  const response = await sendFetch(url, { headers: options.headers });
25
30
  if (response.status >= 400) {
26
- throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
31
+ throw new Error(
32
+ `Failed to download ${url}: ${response.status} ${response.statusText}`
33
+ );
27
34
  }
28
35
  const stream = node_fs.createWriteStream(filePath);
29
36
  await node_util.promisify(node_stream.pipeline)(response.body, stream);
@@ -48,6 +55,9 @@ async function sendFetch(url, options) {
48
55
  const requestOptions = proxy ? { agent: createHttpsProxyAgent(proxy), ...options } : options;
49
56
  return await nodeFetchNative.fetch(url, requestOptions);
50
57
  }
58
+ function cacheDirectory() {
59
+ return process.env.XDG_CACHE_HOME ? pathe.resolve(process.env.XDG_CACHE_HOME, "giget") : pathe.resolve(node_os.homedir(), ".cache/giget");
60
+ }
51
61
  function currentShell() {
52
62
  if (process.env.SHELL) {
53
63
  return process.env.SHELL;
@@ -60,7 +70,9 @@ function currentShell() {
60
70
  function startShell(cwd) {
61
71
  cwd = pathe.resolve(cwd);
62
72
  const shell = currentShell();
63
- console.info(`(experimental) Opening shell in ${pathe.relative(process.cwd(), cwd)}...`);
73
+ console.info(
74
+ `(experimental) Opening shell in ${pathe.relative(process.cwd(), cwd)}...`
75
+ );
64
76
  node_child_process.spawnSync(shell, [], {
65
77
  cwd,
66
78
  shell: true,
@@ -70,24 +82,30 @@ function startShell(cwd) {
70
82
 
71
83
  const github = (input, options) => {
72
84
  const parsed = parseGitURI(input);
85
+ const github2 = process.env.GIGET_GITHUB_URL || "https://github.com";
73
86
  return {
74
87
  name: parsed.repo.replace("/", "-"),
75
88
  version: parsed.ref,
76
89
  subdir: parsed.subdir,
77
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
78
- url: `https://github.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
79
- tar: `https://github.com/${parsed.repo}/archive/${parsed.ref}.tar.gz`
90
+ headers: {
91
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
92
+ },
93
+ url: `${github2}/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
94
+ tar: `${github2}/${parsed.repo}/archive/${parsed.ref}.tar.gz`
80
95
  };
81
96
  };
82
97
  const gitlab = (input, options) => {
83
98
  const parsed = parseGitURI(input);
99
+ const gitlab2 = process.env.GIGET_GITLAB_URL || "https://gitlab.com";
84
100
  return {
85
101
  name: parsed.repo.replace("/", "-"),
86
102
  version: parsed.ref,
87
103
  subdir: parsed.subdir,
88
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
89
- url: `https://gitlab.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
90
- tar: `https://gitlab.com/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
104
+ headers: {
105
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
106
+ },
107
+ url: `${gitlab2}/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
108
+ tar: `${gitlab2}/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
91
109
  };
92
110
  };
93
111
  const bitbucket = (input, options) => {
@@ -96,7 +114,9 @@ const bitbucket = (input, options) => {
96
114
  name: parsed.repo.replace("/", "-"),
97
115
  version: parsed.ref,
98
116
  subdir: parsed.subdir,
99
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
117
+ headers: {
118
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
119
+ },
100
120
  url: `https://bitbucket.com/${parsed.repo}/src/${parsed.ref}${parsed.subdir}`,
101
121
  tar: `https://bitbucket.org/${parsed.repo}/get/${parsed.ref}.tar.gz`
102
122
  };
@@ -107,7 +127,9 @@ const sourcehut = (input, options) => {
107
127
  name: parsed.repo.replace("/", "-"),
108
128
  version: parsed.ref,
109
129
  subdir: parsed.subdir,
110
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
130
+ headers: {
131
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
132
+ },
111
133
  url: `https://git.sr.ht/~${parsed.repo}/tree/${parsed.ref}/item${parsed.subdir}`,
112
134
  tar: `https://git.sr.ht/~${parsed.repo}/archive/${parsed.ref}.tar.gz`
113
135
  };
@@ -121,30 +143,44 @@ const providers = {
121
143
  };
122
144
 
123
145
  const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/unjs/giget/main/templates";
124
- const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
146
+ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY, options) => {
147
+ options = options || {};
125
148
  return async (input) => {
126
149
  const start = Date.now();
127
150
  const registryURL = `${registryEndpoint}/${input}.json`;
128
- const result = await sendFetch(registryURL);
151
+ const result = await sendFetch(registryURL, {
152
+ headers: {
153
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
154
+ }
155
+ });
129
156
  if (result.status >= 400) {
130
- throw new Error(`Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`);
157
+ throw new Error(
158
+ `Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`
159
+ );
131
160
  }
132
161
  const info = await result.json();
133
162
  if (!info.tar || !info.name) {
134
- throw new Error(`Invalid template info from ${registryURL}. name or tar fields are missing!`);
163
+ throw new Error(
164
+ `Invalid template info from ${registryURL}. name or tar fields are missing!`
165
+ );
135
166
  }
136
- debug(`Fetched ${input} template info from ${registryURL} in ${Date.now() - start}ms`);
167
+ debug(
168
+ `Fetched ${input} template info from ${registryURL} in ${Date.now() - start}ms`
169
+ );
137
170
  return info;
138
171
  };
139
172
  };
140
173
 
141
174
  const sourceProtoRe = /^([\w-.]+):/;
142
175
  async function downloadTemplate(input, options = {}) {
143
- options = defu.defu({
144
- registry: process.env.GIGET_REGISTRY,
145
- auth: process.env.GIGET_AUTH
146
- }, options);
147
- const registry = options.registry !== false ? registryProvider(options.registry) : void 0;
176
+ options = defu.defu(
177
+ {
178
+ registry: process.env.GIGET_REGISTRY,
179
+ auth: process.env.GIGET_AUTH
180
+ },
181
+ options
182
+ );
183
+ const registry = options.registry !== false ? registryProvider(options.registry, { auth: options.auth }) : void 0;
148
184
  let providerName = options.provider || (registryProvider ? "registry" : "github");
149
185
  let source = input;
150
186
  const sourceProvierMatch = input.match(sourceProtoRe);
@@ -157,10 +193,15 @@ async function downloadTemplate(input, options = {}) {
157
193
  throw new Error(`Unsupported provider: ${providerName}`);
158
194
  }
159
195
  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}`);
196
+ throw new Error(
197
+ `Failed to download template from ${providerName}: ${error.message}`
198
+ );
161
199
  });
162
200
  template.name = (template.name || "template").replace(/[^\da-z-]/gi, "-");
163
- template.defaultDir = (template.defaultDir || template.name).replace(/[^\da-z-]/gi, "-");
201
+ template.defaultDir = (template.defaultDir || template.name).replace(
202
+ /[^\da-z-]/gi,
203
+ "-"
204
+ );
164
205
  const cwd = pathe.resolve(options.cwd || ".");
165
206
  const extractPath = pathe.resolve(cwd, options.dir || template.defaultDir);
166
207
  if (options.forceClean) {
@@ -170,15 +211,27 @@ async function downloadTemplate(input, options = {}) {
170
211
  throw new Error(`Destination ${extractPath} already exists.`);
171
212
  }
172
213
  await promises.mkdir(extractPath, { recursive: 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");
214
+ const temporaryDirectory = pathe.resolve(
215
+ cacheDirectory(),
216
+ options.provider,
217
+ template.name
218
+ );
219
+ const tarPath = pathe.resolve(
220
+ temporaryDirectory,
221
+ (template.version || template.name) + ".tar.gz"
222
+ );
175
223
  if (options.preferOffline && node_fs.existsSync(tarPath)) {
176
224
  options.offline = true;
177
225
  }
178
226
  if (!options.offline) {
179
227
  await promises.mkdir(pathe.dirname(tarPath), { recursive: true });
180
228
  const s2 = Date.now();
181
- await download(template.tar, tarPath, { headers: template.headers }).catch((error) => {
229
+ await download(template.tar, tarPath, {
230
+ headers: {
231
+ authorization: options.auth ? `Bearer ${options.auth}` : void 0,
232
+ ...template.headers
233
+ }
234
+ }).catch((error) => {
182
235
  if (!node_fs.existsSync(tarPath)) {
183
236
  throw error;
184
237
  }
@@ -188,7 +241,9 @@ async function downloadTemplate(input, options = {}) {
188
241
  debug(`Downloaded ${template.tar} to ${tarPath} in ${Date.now() - s2}ms`);
189
242
  }
190
243
  if (!node_fs.existsSync(tarPath)) {
191
- throw new Error(`Tarball not found: ${tarPath} (offline: ${options.offline})`);
244
+ throw new Error(
245
+ `Tarball not found: ${tarPath} (offline: ${options.offline})`
246
+ );
192
247
  }
193
248
  const s = Date.now();
194
249
  const subdir = template.subdir?.replace(/^\//, "") || "";
@@ -1,19 +1,24 @@
1
1
  import { readFile, writeFile, rm, mkdir } from 'node:fs/promises';
2
- import { homedir } from 'node:os';
3
2
  import { existsSync, createWriteStream, readdirSync } from 'node:fs';
4
3
  import { extract } from 'tar';
5
4
  import { resolve, relative, dirname } from 'pathe';
6
5
  import { defu } from 'defu';
7
6
  import { pipeline } from 'node:stream';
8
7
  import { spawnSync } from 'node:child_process';
8
+ import { homedir } from 'node:os';
9
9
  import { promisify } from 'node:util';
10
10
  import { fetch } from 'node-fetch-native';
11
11
  import createHttpsProxyAgent from 'https-proxy-agent';
12
12
 
13
13
  async function download(url, filePath, options = {}) {
14
14
  const infoPath = filePath + ".json";
15
- const info = JSON.parse(await readFile(infoPath, "utf8").catch(() => "{}"));
16
- const headResponse = await sendFetch(url, { method: "HEAD", headers: options.headers }).catch(() => void 0);
15
+ const info = JSON.parse(
16
+ await readFile(infoPath, "utf8").catch(() => "{}")
17
+ );
18
+ const headResponse = await sendFetch(url, {
19
+ method: "HEAD",
20
+ headers: options.headers
21
+ }).catch(() => void 0);
17
22
  const etag = headResponse?.headers.get("etag");
18
23
  if (info.etag === etag && existsSync(filePath)) {
19
24
  return;
@@ -21,7 +26,9 @@ async function download(url, filePath, options = {}) {
21
26
  info.etag = etag;
22
27
  const response = await sendFetch(url, { headers: options.headers });
23
28
  if (response.status >= 400) {
24
- throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
29
+ throw new Error(
30
+ `Failed to download ${url}: ${response.status} ${response.statusText}`
31
+ );
25
32
  }
26
33
  const stream = createWriteStream(filePath);
27
34
  await promisify(pipeline)(response.body, stream);
@@ -46,6 +53,9 @@ async function sendFetch(url, options) {
46
53
  const requestOptions = proxy ? { agent: createHttpsProxyAgent(proxy), ...options } : options;
47
54
  return await fetch(url, requestOptions);
48
55
  }
56
+ function cacheDirectory() {
57
+ return process.env.XDG_CACHE_HOME ? resolve(process.env.XDG_CACHE_HOME, "giget") : resolve(homedir(), ".cache/giget");
58
+ }
49
59
  function currentShell() {
50
60
  if (process.env.SHELL) {
51
61
  return process.env.SHELL;
@@ -58,7 +68,9 @@ function currentShell() {
58
68
  function startShell(cwd) {
59
69
  cwd = resolve(cwd);
60
70
  const shell = currentShell();
61
- console.info(`(experimental) Opening shell in ${relative(process.cwd(), cwd)}...`);
71
+ console.info(
72
+ `(experimental) Opening shell in ${relative(process.cwd(), cwd)}...`
73
+ );
62
74
  spawnSync(shell, [], {
63
75
  cwd,
64
76
  shell: true,
@@ -68,24 +80,30 @@ function startShell(cwd) {
68
80
 
69
81
  const github = (input, options) => {
70
82
  const parsed = parseGitURI(input);
83
+ const github2 = process.env.GIGET_GITHUB_URL || "https://github.com";
71
84
  return {
72
85
  name: parsed.repo.replace("/", "-"),
73
86
  version: parsed.ref,
74
87
  subdir: parsed.subdir,
75
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
76
- url: `https://github.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
77
- tar: `https://github.com/${parsed.repo}/archive/${parsed.ref}.tar.gz`
88
+ headers: {
89
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
90
+ },
91
+ url: `${github2}/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
92
+ tar: `${github2}/${parsed.repo}/archive/${parsed.ref}.tar.gz`
78
93
  };
79
94
  };
80
95
  const gitlab = (input, options) => {
81
96
  const parsed = parseGitURI(input);
97
+ const gitlab2 = process.env.GIGET_GITLAB_URL || "https://gitlab.com";
82
98
  return {
83
99
  name: parsed.repo.replace("/", "-"),
84
100
  version: parsed.ref,
85
101
  subdir: parsed.subdir,
86
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
87
- url: `https://gitlab.com/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
88
- tar: `https://gitlab.com/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
102
+ headers: {
103
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
104
+ },
105
+ url: `${gitlab2}/${parsed.repo}/tree/${parsed.ref}${parsed.subdir}`,
106
+ tar: `${gitlab2}/${parsed.repo}/-/archive/${parsed.ref}.tar.gz`
89
107
  };
90
108
  };
91
109
  const bitbucket = (input, options) => {
@@ -94,7 +112,9 @@ const bitbucket = (input, options) => {
94
112
  name: parsed.repo.replace("/", "-"),
95
113
  version: parsed.ref,
96
114
  subdir: parsed.subdir,
97
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
115
+ headers: {
116
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
117
+ },
98
118
  url: `https://bitbucket.com/${parsed.repo}/src/${parsed.ref}${parsed.subdir}`,
99
119
  tar: `https://bitbucket.org/${parsed.repo}/get/${parsed.ref}.tar.gz`
100
120
  };
@@ -105,7 +125,9 @@ const sourcehut = (input, options) => {
105
125
  name: parsed.repo.replace("/", "-"),
106
126
  version: parsed.ref,
107
127
  subdir: parsed.subdir,
108
- headers: { Authorization: options.auth ? `Bearer ${options.auth}` : void 0 },
128
+ headers: {
129
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
130
+ },
109
131
  url: `https://git.sr.ht/~${parsed.repo}/tree/${parsed.ref}/item${parsed.subdir}`,
110
132
  tar: `https://git.sr.ht/~${parsed.repo}/archive/${parsed.ref}.tar.gz`
111
133
  };
@@ -119,30 +141,44 @@ const providers = {
119
141
  };
120
142
 
121
143
  const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/unjs/giget/main/templates";
122
- const registryProvider = (registryEndpoint = DEFAULT_REGISTRY) => {
144
+ const registryProvider = (registryEndpoint = DEFAULT_REGISTRY, options) => {
145
+ options = options || {};
123
146
  return async (input) => {
124
147
  const start = Date.now();
125
148
  const registryURL = `${registryEndpoint}/${input}.json`;
126
- const result = await sendFetch(registryURL);
149
+ const result = await sendFetch(registryURL, {
150
+ headers: {
151
+ Authorization: options.auth ? `Bearer ${options.auth}` : void 0
152
+ }
153
+ });
127
154
  if (result.status >= 400) {
128
- throw new Error(`Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`);
155
+ throw new Error(
156
+ `Failed to download ${input} template info from ${registryURL}: ${result.status} ${result.statusText}`
157
+ );
129
158
  }
130
159
  const info = await result.json();
131
160
  if (!info.tar || !info.name) {
132
- throw new Error(`Invalid template info from ${registryURL}. name or tar fields are missing!`);
161
+ throw new Error(
162
+ `Invalid template info from ${registryURL}. name or tar fields are missing!`
163
+ );
133
164
  }
134
- debug(`Fetched ${input} template info from ${registryURL} in ${Date.now() - start}ms`);
165
+ debug(
166
+ `Fetched ${input} template info from ${registryURL} in ${Date.now() - start}ms`
167
+ );
135
168
  return info;
136
169
  };
137
170
  };
138
171
 
139
172
  const sourceProtoRe = /^([\w-.]+):/;
140
173
  async function downloadTemplate(input, options = {}) {
141
- options = defu({
142
- registry: process.env.GIGET_REGISTRY,
143
- auth: process.env.GIGET_AUTH
144
- }, options);
145
- const registry = options.registry !== false ? registryProvider(options.registry) : void 0;
174
+ options = defu(
175
+ {
176
+ registry: process.env.GIGET_REGISTRY,
177
+ auth: process.env.GIGET_AUTH
178
+ },
179
+ options
180
+ );
181
+ const registry = options.registry !== false ? registryProvider(options.registry, { auth: options.auth }) : void 0;
146
182
  let providerName = options.provider || (registryProvider ? "registry" : "github");
147
183
  let source = input;
148
184
  const sourceProvierMatch = input.match(sourceProtoRe);
@@ -155,10 +191,15 @@ async function downloadTemplate(input, options = {}) {
155
191
  throw new Error(`Unsupported provider: ${providerName}`);
156
192
  }
157
193
  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}`);
194
+ throw new Error(
195
+ `Failed to download template from ${providerName}: ${error.message}`
196
+ );
159
197
  });
160
198
  template.name = (template.name || "template").replace(/[^\da-z-]/gi, "-");
161
- template.defaultDir = (template.defaultDir || template.name).replace(/[^\da-z-]/gi, "-");
199
+ template.defaultDir = (template.defaultDir || template.name).replace(
200
+ /[^\da-z-]/gi,
201
+ "-"
202
+ );
162
203
  const cwd = resolve(options.cwd || ".");
163
204
  const extractPath = resolve(cwd, options.dir || template.defaultDir);
164
205
  if (options.forceClean) {
@@ -168,15 +209,27 @@ async function downloadTemplate(input, options = {}) {
168
209
  throw new Error(`Destination ${extractPath} already exists.`);
169
210
  }
170
211
  await mkdir(extractPath, { recursive: true });
171
- const temporaryDirectory = resolve(homedir(), ".giget", options.provider, template.name);
172
- const tarPath = resolve(temporaryDirectory, (template.version || template.name) + ".tar.gz");
212
+ const temporaryDirectory = resolve(
213
+ cacheDirectory(),
214
+ options.provider,
215
+ template.name
216
+ );
217
+ const tarPath = resolve(
218
+ temporaryDirectory,
219
+ (template.version || template.name) + ".tar.gz"
220
+ );
173
221
  if (options.preferOffline && existsSync(tarPath)) {
174
222
  options.offline = true;
175
223
  }
176
224
  if (!options.offline) {
177
225
  await mkdir(dirname(tarPath), { recursive: true });
178
226
  const s2 = Date.now();
179
- await download(template.tar, tarPath, { headers: template.headers }).catch((error) => {
227
+ await download(template.tar, tarPath, {
228
+ headers: {
229
+ authorization: options.auth ? `Bearer ${options.auth}` : void 0,
230
+ ...template.headers
231
+ }
232
+ }).catch((error) => {
180
233
  if (!existsSync(tarPath)) {
181
234
  throw error;
182
235
  }
@@ -186,7 +239,9 @@ async function downloadTemplate(input, options = {}) {
186
239
  debug(`Downloaded ${template.tar} to ${tarPath} in ${Date.now() - s2}ms`);
187
240
  }
188
241
  if (!existsSync(tarPath)) {
189
- throw new Error(`Tarball not found: ${tarPath} (offline: ${options.offline})`);
242
+ throw new Error(
243
+ `Tarball not found: ${tarPath} (offline: ${options.offline})`
244
+ );
190
245
  }
191
246
  const s = Date.now();
192
247
  const subdir = template.subdir?.replace(/^\//, "") || "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "giget",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Download templates and git repositories with pleasure!",
5
5
  "repository": "unjs/giget",
6
6
  "license": "MIT",
@@ -26,32 +26,34 @@
26
26
  "build": "unbuild",
27
27
  "dev": "vitest dev",
28
28
  "giget": "jiti ./src/cli.ts",
29
- "lint": "eslint --ext .ts,.js,.mjs,.cjs .",
29
+ "lint": "eslint --ext .ts,.js,.mjs,.cjs . && prettier -c src test",
30
+ "lint:fix": "eslint --ext .ts,.js,.mjs,.cjs . --fix && prettier -w src test",
30
31
  "prepack": "unbuild",
31
32
  "play": "pnpm giget --force-clean --verbose unjs .tmp/clone",
32
- "release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
33
+ "release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
33
34
  "test": "pnpm lint && vitest run --coverage"
34
35
  },
35
36
  "dependencies": {
36
37
  "colorette": "^2.0.19",
37
- "defu": "^6.1.1",
38
+ "defu": "^6.1.2",
38
39
  "https-proxy-agent": "^5.0.1",
39
40
  "mri": "^1.2.0",
40
- "node-fetch-native": "^1.0.1",
41
- "pathe": "^1.0.0",
42
- "tar": "^6.1.12"
41
+ "node-fetch-native": "^1.0.2",
42
+ "pathe": "^1.1.0",
43
+ "tar": "^6.1.13"
43
44
  },
44
45
  "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"
46
+ "@types/node": "^18.13.0",
47
+ "@types/tar": "^6.1.4",
48
+ "@vitest/coverage-c8": "^0.28.5",
49
+ "changelogen": "^0.4.1",
50
+ "eslint": "^8.34.0",
51
+ "eslint-config-unjs": "^0.1.0",
52
+ "jiti": "^1.17.0",
53
+ "prettier": "^2.8.4",
54
+ "typescript": "^4.9.5",
55
+ "unbuild": "^1.1.1",
56
+ "vitest": "^0.28.5"
55
57
  },
56
- "packageManager": "pnpm@7.16.0"
58
+ "packageManager": "pnpm@7.27.0"
57
59
  }