@temporalio/create 1.0.0-rc.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.
@@ -0,0 +1,38 @@
1
+ // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
2
+ import { ChildProcess, spawn as origSpawn, SpawnOptions } from 'child_process';
3
+
4
+ export class ChildProcessError extends Error {
5
+ public readonly name = 'ChildProcessError';
6
+ public command?: string;
7
+ public args?: ReadonlyArray<string>;
8
+
9
+ constructor(message: string, public readonly code: number | null, public readonly signal: string | null) {
10
+ super(message);
11
+ }
12
+ }
13
+
14
+ export async function spawn(command: string, args?: ReadonlyArray<string>, options?: SpawnOptions): Promise<void> {
15
+ try {
16
+ // Workaround @types/node - avoid choosing overloads per options.stdio variants
17
+ await waitOnChild(options === undefined ? origSpawn(command, args) : origSpawn(command, args || [], options));
18
+ } catch (err) {
19
+ if (err instanceof ChildProcessError) {
20
+ err.command = command;
21
+ err.args = args;
22
+ }
23
+ throw err;
24
+ }
25
+ }
26
+
27
+ export async function waitOnChild(child: ChildProcess): Promise<void> {
28
+ return new Promise((resolve, reject) => {
29
+ child.on('exit', (code, signal) => {
30
+ if (code === 0) {
31
+ resolve();
32
+ } else {
33
+ reject(new ChildProcessError('Process failed', code, signal));
34
+ }
35
+ });
36
+ child.on('error', reject);
37
+ });
38
+ }
@@ -0,0 +1,17 @@
1
+ // Modified from: https://github.com/vercel/next.js/blob/2425f4703c4c6164cecfdb6aa8f80046213f0cc6/packages/create-next-app/helpers/validate-pkg.ts
2
+ import validateProjectName from 'validate-npm-package-name';
3
+
4
+ export function validateNpmName(name: string): {
5
+ valid: boolean;
6
+ problems?: string[];
7
+ } {
8
+ const nameValidation = validateProjectName(name);
9
+ if (nameValidation.validForNewPackages) {
10
+ return { valid: true };
11
+ }
12
+
13
+ return {
14
+ valid: false,
15
+ problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],
16
+ };
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,217 @@
1
+ // Modified from: https://github.com/vercel/next.js/blob/2425f4703c4c6164cecfdb6aa8f80046213f0cc6/packages/create-next-app/index.ts
2
+ import chalk from 'chalk';
3
+ import dedent from 'dedent';
4
+ import { Command } from 'commander';
5
+ import path from 'path';
6
+ import prompts from 'prompts';
7
+ import checkForUpdate from 'update-check';
8
+
9
+ import { createApp } from './create-project.js';
10
+ import { validateNpmName } from './helpers/validate-pkg.js';
11
+ import { fetchSamples } from './helpers/fetch-samples.js';
12
+ import packageJson from './pkg.js';
13
+
14
+ const program = new Command(packageJson.name)
15
+ .version(packageJson.version, '-v, --version', 'Print the version and exit')
16
+ .arguments('[project-directory]')
17
+ .usage(`${chalk.green('[project-directory]')} [options]`)
18
+ .option(
19
+ '-s, --sample <name|github-url>',
20
+ dedent`
21
+ Which sample to bootstrap the project with. You can use the name of a sample
22
+ from https://github.com/temporalio/samples-typescript or use a GitHub URL.
23
+ The URL can have a branch and/or subdirectory—for example:
24
+ https://github.com/temporalio/samples-typescript/tree/next/hello-world
25
+ `
26
+ )
27
+ .option(
28
+ '--sample-path <path-to-sample>',
29
+ dedent`
30
+ In a rare case, your GitHub URL might contain a branch name with
31
+ a slash (e.g. bug/fix-1) and the path to the sample (e.g. foo/bar).
32
+ In this case, you must specify the path to the sample separately:
33
+ --sample-path foo/bar
34
+ `
35
+ )
36
+ .option(
37
+ '-l, --list-samples',
38
+ dedent`
39
+ Print available sample projects and exit
40
+ `
41
+ )
42
+ .option(
43
+ '--use-yarn',
44
+ dedent`
45
+ Use yarn instead of npm
46
+ `
47
+ )
48
+ .option(
49
+ '--git-init',
50
+ dedent`
51
+ Initialize a git repository
52
+ `
53
+ )
54
+ .option(
55
+ '--no-git-init',
56
+ dedent`
57
+ Skip git repository initialization
58
+ `
59
+ )
60
+ .option(
61
+ '--sdk-version <version>',
62
+ dedent`
63
+ Specify which version of the @temporalio/* npm packages to use
64
+ `
65
+ )
66
+ .allowUnknownOption()
67
+ .parse(process.argv);
68
+
69
+ interface Options {
70
+ useYarn?: boolean;
71
+ gitInit?: boolean;
72
+ listSamples?: boolean;
73
+ sample?: string;
74
+ samplePath?: string;
75
+ sdkVersion?: string;
76
+ }
77
+
78
+ let opts: Options;
79
+
80
+ async function start(): Promise<void> {
81
+ opts = program.opts();
82
+ if (opts.listSamples) {
83
+ const samples = await fetchSamples();
84
+ console.log(`Available samples:\n\n${samples.join('\n')}\n`);
85
+ return;
86
+ }
87
+
88
+ let projectPath = program.args[0];
89
+
90
+ if (typeof projectPath === 'string') {
91
+ projectPath = projectPath.trim();
92
+ }
93
+
94
+ if (!projectPath) {
95
+ const res = await prompts({
96
+ type: 'text',
97
+ name: 'path',
98
+ message: 'What is your project named?',
99
+ initial: 'my-temporal',
100
+ validate: (name) => {
101
+ const validation = validateNpmName(path.basename(path.resolve(name)));
102
+ if (validation.valid) {
103
+ return true;
104
+ }
105
+ return 'Invalid project name: ' + validation.problems?.[0];
106
+ },
107
+ });
108
+
109
+ if (typeof res.path === 'string') {
110
+ projectPath = res.path.trim();
111
+ }
112
+ }
113
+
114
+ if (!projectPath) {
115
+ console.error();
116
+ console.error('Please specify the project directory:');
117
+ console.error(` ${chalk.cyan(program.name())} ${chalk.green('<project-directory>')}`);
118
+ console.error();
119
+ console.error('For example:');
120
+ console.error(` ${chalk.cyan(program.name())} ${chalk.green('my-temporal-project')}`);
121
+ console.error();
122
+ console.error(`Run ${chalk.cyan(`${program.name()} --help`)} to see all options.`);
123
+ process.exit(1);
124
+ }
125
+
126
+ const resolvedProjectPath = path.resolve(projectPath);
127
+ const projectName = path.basename(resolvedProjectPath);
128
+
129
+ const { valid, problems } = validateNpmName(projectName);
130
+ if (!valid) {
131
+ console.error(
132
+ `Could not create a project called ${chalk.red(`"${projectName}"`)} because of npm naming restrictions:`
133
+ );
134
+
135
+ problems?.forEach((p) => console.error(` ${chalk.red.bold('*')} ${p}`));
136
+ process.exit(1);
137
+ }
138
+
139
+ let sample = opts.sample;
140
+ if (!sample) {
141
+ const samples = await fetchSamples();
142
+ const choices = samples.map((sample) => ({ title: sample, value: sample }));
143
+
144
+ const res = await prompts({
145
+ type: 'select',
146
+ name: 'sample',
147
+ message: `Which sample would you like to use?`,
148
+ choices,
149
+ initial: samples.indexOf('hello-world'),
150
+ });
151
+
152
+ if (typeof res.sample === 'string') {
153
+ sample = res.sample;
154
+ }
155
+ }
156
+
157
+ if (!sample) {
158
+ console.error();
159
+ console.error('Please specify which sample:');
160
+ console.error(` ${chalk.cyan(program.name())} --sample ${chalk.green('<name|github-url>')}`);
161
+ console.error();
162
+ console.error('For example:');
163
+ console.error(` ${chalk.cyan(program.name())} --sample ${chalk.green('hello-world')}`);
164
+ console.error();
165
+ console.error(`Run ${chalk.cyan(`${program.name()} --help`)} to see all options.`);
166
+ process.exit(1);
167
+ }
168
+
169
+ await createApp({
170
+ appPath: resolvedProjectPath,
171
+ useYarn: !!opts.useYarn,
172
+ gitInit: opts.gitInit,
173
+ sdkVersion: opts.sdkVersion,
174
+ sample: sample.trim(),
175
+ samplePath: typeof opts.samplePath === 'string' ? opts.samplePath.trim() : undefined,
176
+ });
177
+ }
178
+
179
+ const update = checkForUpdate(packageJson).catch(() => null);
180
+
181
+ async function notifyUpdate(): Promise<void> {
182
+ try {
183
+ const res = await update;
184
+ if (res?.latest) {
185
+ console.log();
186
+ console.log(chalk.yellow.bold('A new version of `@temporalio/create` is available!'));
187
+ console.log(
188
+ 'You can update by running: ' +
189
+ chalk.cyan(opts.useYarn ? 'yarn global add @temporalio/create' : 'npm i -g @temporalio/create')
190
+ );
191
+ console.log();
192
+ }
193
+ process.exit();
194
+ } catch {
195
+ // ignore error
196
+ }
197
+ }
198
+
199
+ export function run(): void {
200
+ start()
201
+ .then(notifyUpdate)
202
+ .catch(async (reason) => {
203
+ console.log();
204
+ console.log('Aborting installation.');
205
+ if (reason.command) {
206
+ console.log(` ${chalk.cyan(reason.command)} has failed.`);
207
+ } else {
208
+ console.log(chalk.red('Unexpected error. Please report it as a bug:'));
209
+ console.log(reason);
210
+ }
211
+ console.log();
212
+
213
+ await notifyUpdate();
214
+
215
+ process.exit(1);
216
+ });
217
+ }
package/src/pkg.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { URL } from 'url';
3
+
4
+ const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
5
+
6
+ export default pkg as { name: string; version: string };