wrangler 0.0.0-e1c615f → 0.0.0-e3cab74
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/bin/wrangler.js +2 -2
- package/package.json +28 -13
- package/pages/functions/buildWorker.ts +6 -1
- package/pages/functions/filepath-routing.test.ts +112 -28
- package/pages/functions/filepath-routing.ts +88 -131
- package/pages/functions/routes.ts +11 -18
- package/pages/functions/template-worker.ts +21 -28
- package/src/__tests__/configuration.test.ts +2185 -0
- package/src/__tests__/dev.test.tsx +56 -9
- package/src/__tests__/guess-worker-format.test.ts +66 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +30 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +53 -17
- package/src/__tests__/helpers/mock-console.ts +62 -0
- package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
- package/src/__tests__/helpers/mock-kv.ts +40 -0
- package/src/__tests__/helpers/mock-user.ts +27 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +29 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/index.test.ts +655 -76
- package/src/__tests__/jest.setup.ts +37 -2
- package/src/__tests__/kv.test.ts +441 -398
- package/src/__tests__/logout.test.ts +50 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/parse.test.ts +322 -0
- package/src/__tests__/publish.test.ts +2406 -0
- package/src/__tests__/r2.test.ts +198 -0
- package/src/__tests__/secret.test.ts +326 -0
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +564 -0
- package/src/__tests__/whoami.test.tsx +86 -0
- package/src/api/form_data.ts +112 -26
- package/src/api/preview.ts +34 -15
- package/src/api/worker.ts +58 -27
- package/src/bundle.ts +133 -0
- package/src/cfetch/index.ts +21 -19
- package/src/cfetch/internal.ts +85 -21
- package/src/cli.ts +10 -0
- package/src/config/README.md +107 -0
- package/src/config/config.ts +265 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +305 -0
- package/src/config/index.ts +52 -0
- package/src/config/validation-helpers.ts +370 -0
- package/src/config/validation.ts +1017 -0
- package/src/dev.tsx +455 -368
- package/src/dialogs.tsx +2 -2
- package/src/entry.ts +57 -0
- package/src/environment-variables.ts +33 -0
- package/src/guess-worker-format.ts +68 -0
- package/src/index.tsx +1044 -646
- package/src/inspect.ts +609 -0
- package/src/kv.tsx +47 -39
- package/src/module-collection.ts +142 -36
- package/src/open-in-browser.ts +13 -0
- package/src/package-manager.ts +126 -0
- package/src/pages.tsx +139 -62
- package/src/parse.ts +183 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +350 -77
- package/src/publish.ts +307 -289
- package/src/r2.ts +50 -0
- package/src/reporting.ts +115 -0
- package/src/sites.tsx +183 -56
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +62 -0
- package/src/user.tsx +137 -110
- package/src/whoami.tsx +69 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +127861 -111486
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/run-in-tmp.ts +0 -19
- package/src/__tests__/run-wrangler.ts +0 -32
- package/src/api/inspect.ts +0 -430
- package/src/config.ts +0 -131
- package/src/tail.tsx +0 -73
package/src/kv.tsx
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { URLSearchParams } from "node:url";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
|
|
3
|
+
import type { Config, Environment } from "./config";
|
|
4
4
|
|
|
5
5
|
type KvArgs = {
|
|
6
6
|
binding?: string;
|
|
7
7
|
"namespace-id"?: string;
|
|
8
8
|
env?: string;
|
|
9
9
|
preview?: boolean;
|
|
10
|
-
config?: Config;
|
|
11
10
|
};
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -82,7 +81,7 @@ export interface NamespaceKeyInfo {
|
|
|
82
81
|
export async function listNamespaceKeys(
|
|
83
82
|
accountId: string,
|
|
84
83
|
namespaceId: string,
|
|
85
|
-
prefix
|
|
84
|
+
prefix = ""
|
|
86
85
|
) {
|
|
87
86
|
return await fetchListResult<NamespaceKeyInfo>(
|
|
88
87
|
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/keys`,
|
|
@@ -98,18 +97,31 @@ export async function putKeyValue(
|
|
|
98
97
|
value: string,
|
|
99
98
|
args?: { expiration?: number; expiration_ttl?: number }
|
|
100
99
|
) {
|
|
100
|
+
let searchParams: URLSearchParams | undefined;
|
|
101
|
+
if (args) {
|
|
102
|
+
searchParams = new URLSearchParams();
|
|
103
|
+
if (args.expiration) {
|
|
104
|
+
searchParams.set("expiration", `${args.expiration}`);
|
|
105
|
+
}
|
|
106
|
+
if (args.expiration_ttl) {
|
|
107
|
+
searchParams.set("expiration_ttl", `${args.expiration_ttl}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
101
110
|
return await fetchResult(
|
|
102
111
|
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`,
|
|
103
112
|
{ method: "PUT", body: value },
|
|
104
|
-
|
|
105
|
-
? new URLSearchParams({
|
|
106
|
-
expiration: args.expiration?.toString(),
|
|
107
|
-
expiration_ttl: args.expiration_ttl?.toString(),
|
|
108
|
-
})
|
|
109
|
-
: undefined
|
|
113
|
+
searchParams
|
|
110
114
|
);
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
export async function getKeyValue(
|
|
118
|
+
accountId: string,
|
|
119
|
+
namespaceId: string,
|
|
120
|
+
key: string
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
return await fetchKVGetValue(accountId, namespaceId, key);
|
|
123
|
+
}
|
|
124
|
+
|
|
113
125
|
export async function putBulkKeyValue(
|
|
114
126
|
accountId: string,
|
|
115
127
|
namespaceId: string,
|
|
@@ -140,13 +152,10 @@ export async function deleteBulkKeyValue(
|
|
|
140
152
|
);
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
export function getNamespaceId(
|
|
144
|
-
preview,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"namespace-id": namespaceId,
|
|
148
|
-
env,
|
|
149
|
-
}: KvArgs): string {
|
|
155
|
+
export function getNamespaceId(
|
|
156
|
+
{ preview, binding, "namespace-id": namespaceId, env }: KvArgs,
|
|
157
|
+
configOrEnv: Config | Environment | undefined
|
|
158
|
+
): string {
|
|
150
159
|
// nice
|
|
151
160
|
if (namespaceId) {
|
|
152
161
|
return namespaceId;
|
|
@@ -154,13 +163,13 @@ export function getNamespaceId({
|
|
|
154
163
|
|
|
155
164
|
// begin pre-flight checks
|
|
156
165
|
|
|
157
|
-
// `--binding` is only valid if there's a wrangler configuration
|
|
158
|
-
if (binding && !
|
|
166
|
+
// `--binding` is only valid if there's a wrangler configuration.
|
|
167
|
+
if (binding && !configOrEnv) {
|
|
159
168
|
throw new Error("--binding specified, but no config file was found.");
|
|
160
169
|
}
|
|
161
170
|
|
|
162
171
|
// there's no config. abort here
|
|
163
|
-
if (!
|
|
172
|
+
if (!configOrEnv) {
|
|
164
173
|
throw new Error(
|
|
165
174
|
"Failed to find a config file.\n" +
|
|
166
175
|
"Either use --namespace-id to upload directly or create a configuration file with a binding."
|
|
@@ -169,36 +178,31 @@ export function getNamespaceId({
|
|
|
169
178
|
|
|
170
179
|
// they want to use an environment, actually
|
|
171
180
|
if (env) {
|
|
172
|
-
if (!
|
|
181
|
+
if (!("env" in configOrEnv) || !configOrEnv.env[env]) {
|
|
173
182
|
throw new Error(
|
|
174
183
|
`Failed to find environment "${env}" in configuration file!`
|
|
175
184
|
);
|
|
176
185
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
env: undefined,
|
|
183
|
-
preview,
|
|
184
|
-
config: {
|
|
185
|
-
env: undefined,
|
|
186
|
-
build: undefined,
|
|
187
|
-
name: undefined,
|
|
188
|
-
account_id: undefined,
|
|
189
|
-
...config.env[env],
|
|
186
|
+
return getNamespaceId(
|
|
187
|
+
{
|
|
188
|
+
binding,
|
|
189
|
+
"namespace-id": namespaceId,
|
|
190
|
+
preview,
|
|
190
191
|
},
|
|
191
|
-
|
|
192
|
+
configOrEnv.env[env]
|
|
193
|
+
);
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
// there's no KV namespaces
|
|
195
|
-
if (!
|
|
197
|
+
if (!configOrEnv.kv_namespaces || configOrEnv.kv_namespaces.length === 0) {
|
|
196
198
|
throw new Error(
|
|
197
199
|
"No KV Namespaces configured! Either use --namespace-id to upload directly or add a KV namespace to your wrangler config file."
|
|
198
200
|
);
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
const namespace =
|
|
203
|
+
const namespace = configOrEnv.kv_namespaces.find(
|
|
204
|
+
(ns) => ns.binding === binding
|
|
205
|
+
);
|
|
202
206
|
|
|
203
207
|
// we couldn't find a namespace with that binding
|
|
204
208
|
if (!namespace) {
|
|
@@ -262,6 +266,10 @@ export function getNamespaceId({
|
|
|
262
266
|
/**
|
|
263
267
|
* KV namespace binding names must be valid JS identifiers.
|
|
264
268
|
*/
|
|
265
|
-
export function isValidNamespaceBinding(
|
|
266
|
-
|
|
269
|
+
export function isValidNamespaceBinding(
|
|
270
|
+
binding: string | undefined
|
|
271
|
+
): binding is string {
|
|
272
|
+
return (
|
|
273
|
+
typeof binding === "string" && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(binding)
|
|
274
|
+
);
|
|
267
275
|
}
|
package/src/module-collection.ts
CHANGED
|
@@ -1,21 +1,86 @@
|
|
|
1
|
-
import type { CfModule } from "./api/worker";
|
|
2
|
-
import type esbuild from "esbuild";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import globToRegExp from "glob-to-regexp";
|
|
5
|
+
import type { CfModule, CfModuleType, CfScriptFormat } from "./api/worker";
|
|
6
|
+
import type { Config, ConfigModuleRuleType } from "./config";
|
|
7
|
+
import type esbuild from "esbuild";
|
|
8
|
+
|
|
9
|
+
const RuleTypeToModuleType: Record<ConfigModuleRuleType, CfModuleType> = {
|
|
10
|
+
ESModule: "esm",
|
|
11
|
+
CommonJS: "commonjs",
|
|
12
|
+
CompiledWasm: "compiled-wasm",
|
|
13
|
+
Data: "buffer",
|
|
14
|
+
Text: "text",
|
|
15
|
+
};
|
|
6
16
|
|
|
7
17
|
// This is a combination of an esbuild plugin and a mutable array
|
|
8
18
|
// that we use to collect module references from source code.
|
|
9
19
|
// There will be modules that _shouldn't_ be inlined directly into
|
|
10
20
|
// the bundle. (eg. wasm modules, some text files, etc). We can include
|
|
11
|
-
// those files as modules in the multi part
|
|
21
|
+
// those files as modules in the multi part worker form upload. This
|
|
12
22
|
// plugin+array is used to collect references to these modules, reference
|
|
13
23
|
// them correctly in the bundle, and add them to the form upload.
|
|
14
24
|
|
|
15
|
-
export
|
|
25
|
+
export const DEFAULT_MODULE_RULES: Config["rules"] = [
|
|
26
|
+
{ type: "Text", globs: ["**/*.txt", "**/*.html"] },
|
|
27
|
+
{ type: "Data", globs: ["**/*.bin"] },
|
|
28
|
+
{ type: "CompiledWasm", globs: ["**/*.wasm"] },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default function makeModuleCollector(props: {
|
|
32
|
+
format: CfScriptFormat;
|
|
33
|
+
rules?: Config["rules"];
|
|
34
|
+
}): {
|
|
16
35
|
modules: CfModule[];
|
|
17
36
|
plugin: esbuild.Plugin;
|
|
18
37
|
} {
|
|
38
|
+
const rules: Config["rules"] = [
|
|
39
|
+
...(props.rules || []),
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
41
|
+
...DEFAULT_MODULE_RULES!,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const completedRuleLocations: Record<string, number> = {};
|
|
45
|
+
let index = 0;
|
|
46
|
+
const rulesToRemove: Config["rules"] = [];
|
|
47
|
+
for (const rule of rules) {
|
|
48
|
+
if (rule.type in completedRuleLocations) {
|
|
49
|
+
if (rules[completedRuleLocations[rule.type]].fallthrough !== false) {
|
|
50
|
+
if (index < (props.rules || []).length) {
|
|
51
|
+
console.warn(
|
|
52
|
+
`The module rule at position ${index} (${JSON.stringify(
|
|
53
|
+
rule
|
|
54
|
+
)}) has the same type as a previous rule (at position ${
|
|
55
|
+
completedRuleLocations[rule.type]
|
|
56
|
+
}, ${JSON.stringify(
|
|
57
|
+
rules[completedRuleLocations[rule.type]]
|
|
58
|
+
)}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning.`
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
console.warn(
|
|
62
|
+
`The default module rule ${JSON.stringify(
|
|
63
|
+
rule
|
|
64
|
+
)} has the same type as a previous rule (at position ${
|
|
65
|
+
completedRuleLocations[rule.type]
|
|
66
|
+
}, ${JSON.stringify(
|
|
67
|
+
rules[completedRuleLocations[rule.type]]
|
|
68
|
+
)}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
rulesToRemove.push(rule);
|
|
74
|
+
}
|
|
75
|
+
if (!(rule.type in completedRuleLocations) && rule.fallthrough !== true) {
|
|
76
|
+
completedRuleLocations[rule.type] = index;
|
|
77
|
+
}
|
|
78
|
+
index++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
82
|
+
rulesToRemove.forEach((rule) => rules!.splice(rules!.indexOf(rule), 1));
|
|
83
|
+
|
|
19
84
|
const modules: CfModule[] = [];
|
|
20
85
|
return {
|
|
21
86
|
modules,
|
|
@@ -23,41 +88,82 @@ export default function makeModuleCollector(): {
|
|
|
23
88
|
name: "wrangler-module-collector",
|
|
24
89
|
setup(build) {
|
|
25
90
|
build.onStart(() => {
|
|
26
|
-
// reset the
|
|
91
|
+
// reset the module collection array
|
|
27
92
|
modules.splice(0);
|
|
28
93
|
});
|
|
29
94
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
95
|
+
rules?.forEach((rule) => {
|
|
96
|
+
if (rule.type === "ESModule" || rule.type === "CommonJS") return; // TODO: we should treat these as js files, and use the jsx loader
|
|
97
|
+
|
|
98
|
+
rule.globs.forEach((glob) => {
|
|
99
|
+
build.onResolve(
|
|
100
|
+
{ filter: globToRegExp(glob) },
|
|
101
|
+
async (args: esbuild.OnResolveArgs) => {
|
|
102
|
+
// take the file and massage it to a
|
|
103
|
+
// transportable/manageable format
|
|
104
|
+
|
|
105
|
+
const filePath = path.join(args.resolveDir, args.path);
|
|
106
|
+
const fileContent = await readFile(filePath);
|
|
107
|
+
const fileHash = crypto
|
|
108
|
+
.createHash("sha1")
|
|
109
|
+
.update(fileContent)
|
|
110
|
+
.digest("hex");
|
|
111
|
+
const fileName = `./${fileHash}-${path.basename(args.path)}`;
|
|
45
112
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
// add the module to the array
|
|
114
|
+
modules.push({
|
|
115
|
+
name: fileName,
|
|
116
|
+
content: fileContent,
|
|
117
|
+
type: RuleTypeToModuleType[rule.type],
|
|
118
|
+
});
|
|
52
119
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
120
|
+
return {
|
|
121
|
+
path: fileName, // change the reference to the changed module
|
|
122
|
+
external: props.format === "modules", // mark it as external in the bundle
|
|
123
|
+
namespace: `wrangler-module-${rule.type}`, // just a tag, this isn't strictly necessary
|
|
124
|
+
watchFiles: [filePath], // we also add the file to esbuild's watch list
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
if (props.format === "service-worker") {
|
|
130
|
+
build.onLoad(
|
|
131
|
+
{ filter: globToRegExp(glob) },
|
|
132
|
+
async (args: esbuild.OnLoadArgs) => {
|
|
133
|
+
return {
|
|
134
|
+
// We replace the the module with an identifier
|
|
135
|
+
// that we'll separately add to the form upload
|
|
136
|
+
// as part of [wasm_modules]/[text_blobs]. This identifier has to be a valid
|
|
137
|
+
// JS identifier, so we replace all non alphanumeric characters
|
|
138
|
+
// with an underscore.
|
|
139
|
+
// TODO: what of "Data"?
|
|
140
|
+
contents: `export default ${args.path.replace(
|
|
141
|
+
/[^a-zA-Z0-9_$]/g,
|
|
142
|
+
"_"
|
|
143
|
+
)};`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
rulesToRemove.forEach((rule) => {
|
|
152
|
+
rule.globs.forEach((glob) => {
|
|
153
|
+
build.onResolve(
|
|
154
|
+
{ filter: globToRegExp(glob) },
|
|
155
|
+
async (args: esbuild.OnResolveArgs) => {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`The file ${
|
|
158
|
+
args.path
|
|
159
|
+
} matched a module rule in your configuration (${JSON.stringify(
|
|
160
|
+
rule
|
|
161
|
+
)}), but was ignored because a previous rule with the same type was not marked as \`fallthrough = true\`.`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
61
167
|
},
|
|
62
168
|
},
|
|
63
169
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import open from "open";
|
|
2
|
+
/**
|
|
3
|
+
* An extremely simple wrapper around the open command.
|
|
4
|
+
* Specifically, it adds an 'error' event handler so that when this function
|
|
5
|
+
* is called in environments where we can't open the browser (e.g. github codespaces,
|
|
6
|
+
* stackblitz, remote servers), it doesn't just crash the process.
|
|
7
|
+
*/
|
|
8
|
+
export default async function openInBrowser(url: string): Promise<void> {
|
|
9
|
+
const childProcess = await open(url);
|
|
10
|
+
childProcess.on("error", () => {
|
|
11
|
+
console.warn(`Failed to open ${url} in a browser`);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { execa, execaCommandSync } from "execa";
|
|
4
|
+
|
|
5
|
+
export interface PackageManager {
|
|
6
|
+
cwd: string;
|
|
7
|
+
type: "npm" | "yarn";
|
|
8
|
+
addDevDeps(...packages: string[]): Promise<void>;
|
|
9
|
+
install(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function getPackageManager(cwd: string): Promise<PackageManager> {
|
|
13
|
+
const [hasYarn, hasNpm] = await Promise.all([supportsYarn(), supportsNpm()]);
|
|
14
|
+
const hasYarnLock = existsSync(join(cwd, "yarn.lock"));
|
|
15
|
+
const hasNpmLock = existsSync(join(cwd, "package-lock.json"));
|
|
16
|
+
|
|
17
|
+
if (hasNpmLock) {
|
|
18
|
+
if (hasNpm) {
|
|
19
|
+
console.log(
|
|
20
|
+
"Using npm as package manager, as there is already a package-lock.json file."
|
|
21
|
+
);
|
|
22
|
+
return { ...NpmPackageManager, cwd };
|
|
23
|
+
} else if (hasYarn) {
|
|
24
|
+
console.log("Using yarn as package manager.");
|
|
25
|
+
console.warn(
|
|
26
|
+
"There is already a package-lock.json file but could not find npm on the PATH."
|
|
27
|
+
);
|
|
28
|
+
return { ...YarnPackageManager, cwd };
|
|
29
|
+
}
|
|
30
|
+
} else if (hasYarnLock) {
|
|
31
|
+
if (hasYarn) {
|
|
32
|
+
console.log(
|
|
33
|
+
"Using yarn as package manager, as there is already a yarn.lock file."
|
|
34
|
+
);
|
|
35
|
+
return { ...YarnPackageManager, cwd };
|
|
36
|
+
} else if (hasNpm) {
|
|
37
|
+
console.log("Using npm as package manager.");
|
|
38
|
+
console.warn(
|
|
39
|
+
"There is already a yarn.lock file but could not find yarn on the PATH."
|
|
40
|
+
);
|
|
41
|
+
return { ...NpmPackageManager, cwd };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (hasNpm) {
|
|
46
|
+
console.log("Using npm as package manager.");
|
|
47
|
+
return { ...NpmPackageManager, cwd };
|
|
48
|
+
} else if (hasYarn) {
|
|
49
|
+
console.log("Using yarn as package manager.");
|
|
50
|
+
return { ...YarnPackageManager, cwd };
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"Unable to find a package manager. Supported managers are: npm and yarn."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the name of the given `packageManager`.
|
|
60
|
+
*/
|
|
61
|
+
export function getPackageManagerName(packageManager: PackageManager): string {
|
|
62
|
+
return packageManager.type ?? "unknown";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Manage packages using npm
|
|
67
|
+
*/
|
|
68
|
+
const NpmPackageManager: PackageManager = {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
type: "npm",
|
|
71
|
+
/** Add and install a new devDependency into the local package.json. */
|
|
72
|
+
async addDevDeps(...packages: string[]): Promise<void> {
|
|
73
|
+
await execa("npm", ["install", ...packages, "--save-dev"], {
|
|
74
|
+
stdio: "inherit",
|
|
75
|
+
cwd: this.cwd,
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/** Install all the dependencies in the local package.json. */
|
|
80
|
+
async install(): Promise<void> {
|
|
81
|
+
await execa("npm", ["install"], {
|
|
82
|
+
stdio: "inherit",
|
|
83
|
+
cwd: this.cwd,
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Manage packages using yarn
|
|
90
|
+
*/
|
|
91
|
+
const YarnPackageManager: PackageManager = {
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
type: "yarn",
|
|
94
|
+
/** Add and install a new devDependency into the local package.json. */
|
|
95
|
+
async addDevDeps(...packages: string[]): Promise<void> {
|
|
96
|
+
await execa("yarn", ["add", ...packages, "--dev"], {
|
|
97
|
+
stdio: "inherit",
|
|
98
|
+
cwd: this.cwd,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/** Install all the dependencies in the local package.json. */
|
|
103
|
+
async install(): Promise<void> {
|
|
104
|
+
await execa("yarn", ["install"], {
|
|
105
|
+
stdio: "inherit",
|
|
106
|
+
cwd: this.cwd,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
async function supports(name: string): Promise<boolean> {
|
|
112
|
+
try {
|
|
113
|
+
execaCommandSync(`${name} --version`, { stdio: "ignore" });
|
|
114
|
+
return true;
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function supportsYarn(): Promise<boolean> {
|
|
121
|
+
return supports("yarn");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function supportsNpm(): Promise<boolean> {
|
|
125
|
+
return supports("npm");
|
|
126
|
+
}
|