create-convex-monorepo 0.2.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/CONTRIBUTING.md +55 -0
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/assets/backend/PROVENANCE.md +3 -0
- package/assets/backend/convex/_generated/api.d.ts +51 -0
- package/assets/backend/convex/_generated/api.js +23 -0
- package/assets/backend/convex/_generated/dataModel.d.ts +60 -0
- package/assets/backend/convex/_generated/server.d.ts +162 -0
- package/assets/backend/convex/_generated/server.js +101 -0
- package/assets/backend/convex/messages.ts +38 -0
- package/assets/backend/convex/schema.ts +9 -0
- package/assets/backend/convex/tsconfig.json +26 -0
- package/assets/backend-blank/PROVENANCE.md +3 -0
- package/assets/backend-blank/convex/_generated/api.d.ts +45 -0
- package/assets/backend-blank/convex/_generated/api.js +23 -0
- package/assets/backend-blank/convex/_generated/dataModel.d.ts +60 -0
- package/assets/backend-blank/convex/_generated/server.d.ts +162 -0
- package/assets/backend-blank/convex/_generated/server.js +101 -0
- package/assets/backend-blank/convex/schema.ts +3 -0
- package/assets/setup/convex-setup.mjs +250 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/create.d.ts +8 -0
- package/dist/commands/create.js +182 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/generator/context.d.ts +3 -0
- package/dist/generator/context.js +81 -0
- package/dist/generator/context.js.map +1 -0
- package/dist/generator/index.d.ts +12 -0
- package/dist/generator/index.js +123 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/options.d.ts +15 -0
- package/dist/generator/options.js +85 -0
- package/dist/generator/options.js.map +1 -0
- package/dist/generator/types.d.ts +47 -0
- package/dist/generator/types.js +2 -0
- package/dist/generator/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/auth/clerk/index.d.ts +2 -0
- package/dist/integrations/auth/clerk/index.js +114 -0
- package/dist/integrations/auth/clerk/index.js.map +1 -0
- package/dist/integrations/auth/index.d.ts +2 -0
- package/dist/integrations/auth/index.js +7 -0
- package/dist/integrations/auth/index.js.map +1 -0
- package/dist/integrations/auth/none/index.d.ts +2 -0
- package/dist/integrations/auth/none/index.js +18 -0
- package/dist/integrations/auth/none/index.js.map +1 -0
- package/dist/integrations/auth/shared.d.ts +11 -0
- package/dist/integrations/auth/shared.js +40 -0
- package/dist/integrations/auth/shared.js.map +1 -0
- package/dist/package-manager/index.d.ts +6 -0
- package/dist/package-manager/index.js +22 -0
- package/dist/package-manager/index.js.map +1 -0
- package/dist/templates/apps/expo/index.d.ts +2 -0
- package/dist/templates/apps/expo/index.js +91 -0
- package/dist/templates/apps/expo/index.js.map +1 -0
- package/dist/templates/apps/index.d.ts +2 -0
- package/dist/templates/apps/index.js +11 -0
- package/dist/templates/apps/index.js.map +1 -0
- package/dist/templates/apps/next/index.d.ts +2 -0
- package/dist/templates/apps/next/index.js +66 -0
- package/dist/templates/apps/next/index.js.map +1 -0
- package/dist/templates/apps/shared.d.ts +24 -0
- package/dist/templates/apps/shared.js +124 -0
- package/dist/templates/apps/shared.js.map +1 -0
- package/dist/templates/apps/tanstack-start/index.d.ts +2 -0
- package/dist/templates/apps/tanstack-start/index.js +82 -0
- package/dist/templates/apps/tanstack-start/index.js.map +1 -0
- package/dist/templates/apps/vite/index.d.ts +2 -0
- package/dist/templates/apps/vite/index.js +42 -0
- package/dist/templates/apps/vite/index.js.map +1 -0
- package/dist/templates/backend/index.d.ts +2 -0
- package/dist/templates/backend/index.js +58 -0
- package/dist/templates/backend/index.js.map +1 -0
- package/dist/templates/root/index.d.ts +2 -0
- package/dist/templates/root/index.js +178 -0
- package/dist/templates/root/index.js.map +1 -0
- package/dist/templates/versions.d.ts +18 -0
- package/dist/templates/versions.js +19 -0
- package/dist/templates/versions.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/docs/adding-a-framework.md +27 -0
- package/docs/adding-an-auth-provider.md +31 -0
- package/docs/architecture.md +49 -0
- package/docs/releases.md +78 -0
- package/docs/research.md +76 -0
- package/docs/verification.md +78 -0
- package/package.json +77 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { lstat, mkdir, mkdtemp, readdir, copyFile, unlink, rm, rmdir, } from 'node:fs/promises';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { createContext } from './context.js';
|
|
5
|
+
import { normalizeOptions } from './options.js';
|
|
6
|
+
import { generateRoot } from '../templates/root/index.js';
|
|
7
|
+
import { generateBackend } from '../templates/backend/index.js';
|
|
8
|
+
import { appTemplates } from '../templates/apps/index.js';
|
|
9
|
+
import { authAdapters } from '../integrations/auth/index.js';
|
|
10
|
+
import { pnpm, runCommand } from '../package-manager/index.js';
|
|
11
|
+
export { normalizeOptions, validateProjectName } from './options.js';
|
|
12
|
+
export function selectTemplate(framework) {
|
|
13
|
+
const template = appTemplates[framework];
|
|
14
|
+
if (!template)
|
|
15
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
16
|
+
return template;
|
|
17
|
+
}
|
|
18
|
+
export async function generateProject(raw, settings = {}) {
|
|
19
|
+
settings.signal?.throwIfAborted();
|
|
20
|
+
const options = normalizeOptions(raw);
|
|
21
|
+
const cwd = resolve(settings.cwd ?? process.cwd());
|
|
22
|
+
const target = join(cwd, options.name);
|
|
23
|
+
let existingEmpty = false;
|
|
24
|
+
let originalIdentity;
|
|
25
|
+
try {
|
|
26
|
+
const stat = await lstat(target);
|
|
27
|
+
if (stat.isSymbolicLink() ||
|
|
28
|
+
!stat.isDirectory() ||
|
|
29
|
+
(await readdir(target)).length > 0)
|
|
30
|
+
throw new Error(`Refusing to overwrite ${target}. Choose a new or empty directory.`);
|
|
31
|
+
existingEmpty = true;
|
|
32
|
+
originalIdentity = { dev: stat.dev, ino: stat.ino };
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code !== 'ENOENT')
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
await mkdir(cwd, { recursive: true });
|
|
39
|
+
const staging = await mkdtemp(join(cwd, `.${options.name}-`));
|
|
40
|
+
let createdTarget = false;
|
|
41
|
+
const committed = [];
|
|
42
|
+
async function commitDirectory(source, destination) {
|
|
43
|
+
for (const entry of await readdir(source, { withFileTypes: true })) {
|
|
44
|
+
settings.signal?.throwIfAborted();
|
|
45
|
+
const from = join(source, entry.name);
|
|
46
|
+
const to = join(destination, entry.name);
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
await mkdir(to);
|
|
49
|
+
committed.push({ path: to, directory: true });
|
|
50
|
+
await commitDirectory(from, to);
|
|
51
|
+
}
|
|
52
|
+
else if (entry.isFile()) {
|
|
53
|
+
await copyFile(from, to, constants.COPYFILE_EXCL);
|
|
54
|
+
committed.push({ path: to, directory: false });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error(`Unsupported generated file: ${from}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const context = createContext(staging, options);
|
|
63
|
+
await generateRoot(context);
|
|
64
|
+
await generateBackend(context);
|
|
65
|
+
settings.onProgress?.('Added packages/backend');
|
|
66
|
+
for (const app of options.apps) {
|
|
67
|
+
settings.signal?.throwIfAborted();
|
|
68
|
+
await selectTemplate(app.framework).generate(context, app);
|
|
69
|
+
settings.onProgress?.(`Added apps/${app.name}`);
|
|
70
|
+
}
|
|
71
|
+
await authAdapters[options.auth].apply(context);
|
|
72
|
+
settings.signal?.throwIfAborted();
|
|
73
|
+
if (existingEmpty) {
|
|
74
|
+
const current = await lstat(target);
|
|
75
|
+
if (current.isSymbolicLink() ||
|
|
76
|
+
current.dev !== originalIdentity?.dev ||
|
|
77
|
+
current.ino !== originalIdentity.ino ||
|
|
78
|
+
(await readdir(target)).length) {
|
|
79
|
+
throw new Error(`Destination changed during generation: ${target}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await mkdir(target);
|
|
84
|
+
createdTarget = true;
|
|
85
|
+
}
|
|
86
|
+
// Exclusive copies work on Windows too, where renaming over an existing
|
|
87
|
+
// directory fails. Roll back only entries created by this invocation.
|
|
88
|
+
await commitDirectory(staging, target);
|
|
89
|
+
await rm(staging, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
for (const entry of committed.reverse()) {
|
|
93
|
+
await (entry.directory ? rmdir(entry.path) : unlink(entry.path)).catch(() => undefined);
|
|
94
|
+
}
|
|
95
|
+
await rm(staging, { recursive: true, force: true });
|
|
96
|
+
if (createdTarget)
|
|
97
|
+
await rmdir(target).catch(() => undefined);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
let recovery = 'pnpm install';
|
|
101
|
+
try {
|
|
102
|
+
if (options.install) {
|
|
103
|
+
await pnpm.install(target, settings.signal);
|
|
104
|
+
settings.onProgress?.('Installed dependencies');
|
|
105
|
+
}
|
|
106
|
+
if (options.git) {
|
|
107
|
+
recovery = 'git init';
|
|
108
|
+
await runCommand('git', ['init'], target, settings.signal);
|
|
109
|
+
settings.onProgress?.('Initialized git');
|
|
110
|
+
}
|
|
111
|
+
if (options.initConvex) {
|
|
112
|
+
recovery = 'pnpm convex:setup';
|
|
113
|
+
settings.onProgress?.('Starting Convex setup');
|
|
114
|
+
await runCommand('pnpm', ['convex:setup'], target, settings.signal);
|
|
115
|
+
settings.onProgress?.('Initialized Convex and linked frontend URLs');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw new Error(`Project files are ready at ${target}, but setup failed. ${error instanceof Error ? error.message : String(error)}\nRun ${recovery} in that directory to retry.`, { cause: error });
|
|
120
|
+
}
|
|
121
|
+
return target;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AppSpec, Framework, ProjectOptions } from './types.js';
|
|
2
|
+
export interface RawOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
apps?: string | AppSpec[];
|
|
5
|
+
auth?: string;
|
|
6
|
+
example?: string;
|
|
7
|
+
packageManager?: string;
|
|
8
|
+
install?: boolean;
|
|
9
|
+
initConvex?: boolean;
|
|
10
|
+
git?: boolean;
|
|
11
|
+
yes?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const frameworks: readonly Framework[];
|
|
14
|
+
export declare function validateProjectName(name: string): string | undefined;
|
|
15
|
+
export declare function normalizeOptions(raw: RawOptions): ProjectOptions;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const frameworks = [
|
|
2
|
+
'next',
|
|
3
|
+
'vite',
|
|
4
|
+
'tanstack-start',
|
|
5
|
+
'expo',
|
|
6
|
+
];
|
|
7
|
+
const reserved = /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9]|node_modules)$/i;
|
|
8
|
+
export function validateProjectName(name) {
|
|
9
|
+
if (name.length > 100 ||
|
|
10
|
+
!/^[a-z0-9][a-z0-9-]*$/.test(name) ||
|
|
11
|
+
reserved.test(name)) {
|
|
12
|
+
return 'Use a lowercase name of 1–100 letters, numbers, or hyphens, starting with a letter or number. Paths and reserved names are not allowed.';
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export function normalizeOptions(raw) {
|
|
17
|
+
const name = raw.name ?? 'my-app';
|
|
18
|
+
const error = validateProjectName(name);
|
|
19
|
+
if (error)
|
|
20
|
+
throw new Error(`Invalid project name "${name}". ${error}`);
|
|
21
|
+
if (raw.packageManager !== undefined && raw.packageManager !== 'pnpm')
|
|
22
|
+
throw new Error('Only pnpm is supported in v0.1.');
|
|
23
|
+
const initConvex = raw.initConvex ?? false;
|
|
24
|
+
if (initConvex && raw.install === false)
|
|
25
|
+
throw new Error('--init-convex requires dependencies. Omit --no-install or use --no-init-convex.');
|
|
26
|
+
const example = raw.example ?? 'messages';
|
|
27
|
+
if (example !== 'none' && example !== 'messages')
|
|
28
|
+
throw new Error(`Unknown example "${example}". Choose none or messages.`);
|
|
29
|
+
const auth = raw.auth ?? 'none';
|
|
30
|
+
if (auth !== 'none' && auth !== 'clerk')
|
|
31
|
+
throw new Error(`Unknown auth provider "${auth}". Choose none or clerk.`);
|
|
32
|
+
const used = new Set();
|
|
33
|
+
const input = raw.apps ?? 'next';
|
|
34
|
+
const entries = typeof input === 'string'
|
|
35
|
+
? input.split(',').map((entry) => {
|
|
36
|
+
const parts = entry.trim().split(':');
|
|
37
|
+
if (parts.length > 2 || parts.some((part) => !part))
|
|
38
|
+
throw new Error(`Invalid app selection "${entry}". Use framework or name:framework.`);
|
|
39
|
+
return {
|
|
40
|
+
name: parts.length === 2 ? parts[0] : undefined,
|
|
41
|
+
framework: parts.at(-1),
|
|
42
|
+
};
|
|
43
|
+
})
|
|
44
|
+
: input.map((app) => ({ ...app }));
|
|
45
|
+
if (!entries.length)
|
|
46
|
+
throw new Error('Select at least one application.');
|
|
47
|
+
const apps = entries.map((entry) => {
|
|
48
|
+
if (!frameworks.includes(entry.framework))
|
|
49
|
+
throw new Error(`Unknown framework "${entry.framework}". Choose ${frameworks.join(', ')}.`);
|
|
50
|
+
const framework = entry.framework;
|
|
51
|
+
let appName = entry.name;
|
|
52
|
+
if (appName === undefined) {
|
|
53
|
+
const base = framework === 'expo'
|
|
54
|
+
? 'mobile'
|
|
55
|
+
: used.has('web')
|
|
56
|
+
? framework === 'vite'
|
|
57
|
+
? 'admin'
|
|
58
|
+
: 'app'
|
|
59
|
+
: 'web';
|
|
60
|
+
appName = base;
|
|
61
|
+
for (let suffix = 2; used.has(appName); suffix++)
|
|
62
|
+
appName = `${base}-${suffix}`;
|
|
63
|
+
}
|
|
64
|
+
if (validateProjectName(appName))
|
|
65
|
+
throw new Error(`Invalid application name "${appName}".`);
|
|
66
|
+
if (['backend', 'typescript-config', 'eslint-config'].includes(appName)) {
|
|
67
|
+
throw new Error(`Application name "${appName}" is reserved for a shared package.`);
|
|
68
|
+
}
|
|
69
|
+
if (used.has(appName))
|
|
70
|
+
throw new Error(`Duplicate application name "${appName}".`);
|
|
71
|
+
used.add(appName);
|
|
72
|
+
return { name: appName, framework };
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
apps,
|
|
77
|
+
auth: auth,
|
|
78
|
+
example,
|
|
79
|
+
packageManager: 'pnpm',
|
|
80
|
+
install: raw.install ?? (initConvex || raw.yes || false),
|
|
81
|
+
initConvex,
|
|
82
|
+
git: raw.git ?? raw.yes ?? false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/generator/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type Framework = 'next' | 'vite' | 'tanstack-start' | 'expo';
|
|
2
|
+
export type Auth = 'none' | 'clerk';
|
|
3
|
+
export type Example = 'none' | 'messages';
|
|
4
|
+
export interface AppSpec {
|
|
5
|
+
name: string;
|
|
6
|
+
framework: Framework;
|
|
7
|
+
}
|
|
8
|
+
export interface ProjectOptions {
|
|
9
|
+
name: string;
|
|
10
|
+
apps: AppSpec[];
|
|
11
|
+
auth: Auth;
|
|
12
|
+
example: Example;
|
|
13
|
+
packageManager: 'pnpm';
|
|
14
|
+
install: boolean;
|
|
15
|
+
initConvex: boolean;
|
|
16
|
+
git: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface PackageManifest {
|
|
19
|
+
name: string;
|
|
20
|
+
version?: string;
|
|
21
|
+
private?: boolean;
|
|
22
|
+
type?: 'module' | 'commonjs';
|
|
23
|
+
main?: string;
|
|
24
|
+
scripts?: Record<string, string>;
|
|
25
|
+
dependencies?: Record<string, string>;
|
|
26
|
+
devDependencies?: Record<string, string>;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface GeneratorContext {
|
|
30
|
+
options: ProjectOptions;
|
|
31
|
+
/** Filesystem staging directory owned by this generation. */
|
|
32
|
+
root: string;
|
|
33
|
+
scope: string;
|
|
34
|
+
write(path: string, contents: string): Promise<void>;
|
|
35
|
+
json(path: string, value: unknown): Promise<void>;
|
|
36
|
+
mergePackage(path: string, patch: Partial<PackageManifest>): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export interface AppTemplate {
|
|
39
|
+
id: Framework;
|
|
40
|
+
label: string;
|
|
41
|
+
generate(context: GeneratorContext, app: AppSpec): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export interface AuthAdapter {
|
|
44
|
+
id: Auth;
|
|
45
|
+
label: string;
|
|
46
|
+
apply(context: GeneratorContext): Promise<void>;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/generator/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { generateProject } from './generator/index.js';
|
|
2
|
+
export { normalizeOptions } from './generator/options.js';
|
|
3
|
+
export type { GeneratorContext, ProjectOptions, AppSpec, AppTemplate, AuthAdapter, Framework, Auth, Example, } from './generator/types.js';
|
|
4
|
+
export type { RawOptions } from './generator/options.js';
|
|
5
|
+
export type { GenerateSettings } from './generator/index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { versions as v } from '../../../templates/versions.js';
|
|
2
|
+
import { platform, writeProviders } from '../shared.js';
|
|
3
|
+
const bindings = {
|
|
4
|
+
next: { sdk: '@clerk/nextjs', version: v.clerkNext },
|
|
5
|
+
vite: { sdk: '@clerk/react', version: v.clerkReact },
|
|
6
|
+
'tanstack-start': { sdk: '@clerk/tanstack-react-start', version: '1.5.12' },
|
|
7
|
+
expo: { sdk: '@clerk/expo', version: v.clerkExpo },
|
|
8
|
+
};
|
|
9
|
+
export const clerkAdapter = {
|
|
10
|
+
id: 'clerk',
|
|
11
|
+
label: 'Clerk',
|
|
12
|
+
async apply(ctx) {
|
|
13
|
+
if (ctx.options.example === 'messages')
|
|
14
|
+
await ctx.write('packages/backend/convex/access.ts', `import type { QueryCtx, MutationCtx } from './_generated/server';
|
|
15
|
+
export async function getOwner(ctx: QueryCtx | MutationCtx): Promise<string> {
|
|
16
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
17
|
+
if (!identity) throw new Error('Sign in to access messages.');
|
|
18
|
+
return identity.tokenIdentifier;
|
|
19
|
+
}
|
|
20
|
+
`);
|
|
21
|
+
await ctx.write('packages/backend/convex/auth.config.ts', `import type { AuthConfig } from 'convex/server';
|
|
22
|
+
const domain = process.env.CLERK_JWT_ISSUER_DOMAIN;
|
|
23
|
+
if (!domain) throw new Error('Set CLERK_JWT_ISSUER_DOMAIN on this Convex deployment. See README.md.');
|
|
24
|
+
export default { providers: [{ domain, applicationID: 'convex' }] } satisfies AuthConfig;
|
|
25
|
+
`);
|
|
26
|
+
await ctx.write('packages/backend/.env.clerk.example', '# Configure this on the Convex deployment with convex env set, not in frontend env files.\n# CLERK_JWT_ISSUER_DOMAIN=https://your-instance.clerk.accounts.dev\n');
|
|
27
|
+
for (const app of ctx.options.apps) {
|
|
28
|
+
const dir = `apps/${app.name}`;
|
|
29
|
+
const binding = bindings[app.framework];
|
|
30
|
+
const { native, prefix } = platform(app);
|
|
31
|
+
await ctx.mergePackage(`${dir}/package.json`, {
|
|
32
|
+
dependencies: {
|
|
33
|
+
[binding.sdk]: binding.version,
|
|
34
|
+
...(native
|
|
35
|
+
? {
|
|
36
|
+
'expo-secure-store': '57.0.3',
|
|
37
|
+
'expo-auth-session': '57.0.11',
|
|
38
|
+
'expo-web-browser': '57.0.2',
|
|
39
|
+
'expo-crypto': '57.0.2',
|
|
40
|
+
'expo-constants': '57.0.17',
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
await ctx.write(`${dir}/.env.clerk.example`, `# Append these values to .env.local alongside the Convex URL.\n${prefix}_CLERK_PUBLISHABLE_KEY=\n${app.framework === 'next' || app.framework === 'tanstack-start' ? '# Server only. Never prefix this with NEXT_PUBLIC_, VITE_ or EXPO_PUBLIC_.\nCLERK_SECRET_KEY=\n' : ''}${native ? `# Enable Google OAuth and Native API in Clerk.\n# Register redirect URL: ccm-${ctx.options.name}-${app.name}://continue\n# Build a development client with pnpm ios or pnpm android for this scheme.\n` : ''}`);
|
|
46
|
+
await writeProviders(ctx, app, {
|
|
47
|
+
sdk: binding.sdk,
|
|
48
|
+
...(native
|
|
49
|
+
? {
|
|
50
|
+
extraImports: "import { tokenCache } from '@clerk/expo/token-cache';",
|
|
51
|
+
providerProps: 'tokenCache={tokenCache}',
|
|
52
|
+
}
|
|
53
|
+
: {}),
|
|
54
|
+
});
|
|
55
|
+
if (app.framework === 'next') {
|
|
56
|
+
await ctx.write(`${dir}/src/proxy.ts`, `import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
57
|
+
export default clerkMiddleware();
|
|
58
|
+
export const config = { matcher: ['/((?!_next|[^?]*\\\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', '/(api|trpc)(.*)'] };
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
if (app.framework === 'tanstack-start') {
|
|
62
|
+
await ctx.write(`${dir}/src/start.ts`, `import { createStart } from '@tanstack/react-start';
|
|
63
|
+
import { clerkMiddleware } from '@clerk/tanstack-react-start/server';
|
|
64
|
+
export const startInstance = createStart(() => ({ requestMiddleware: [clerkMiddleware()] }));
|
|
65
|
+
`);
|
|
66
|
+
}
|
|
67
|
+
if (!native) {
|
|
68
|
+
await ctx.write(`${dir}/src/auth-controls.tsx`, `'use client';
|
|
69
|
+
import { useAuth, SignInButton, UserButton } from '${binding.sdk}';
|
|
70
|
+
export function AuthControls() {
|
|
71
|
+
const { isLoaded, isSignedIn } = useAuth();
|
|
72
|
+
if (!isLoaded) return <p>Loading sign-in…</p>;
|
|
73
|
+
return isSignedIn ? <UserButton /> : <SignInButton mode="modal"><button type="button">Sign in</button></SignInButton>;
|
|
74
|
+
}
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
await ctx.write(`${dir}/src/auth-controls.tsx`, `import { useState } from 'react';
|
|
79
|
+
import { Button, Text, View } from 'react-native';
|
|
80
|
+
import { useAuth, useSSO } from '@clerk/expo';
|
|
81
|
+
import * as AuthSession from 'expo-auth-session';
|
|
82
|
+
import * as WebBrowser from 'expo-web-browser';
|
|
83
|
+
WebBrowser.maybeCompleteAuthSession();
|
|
84
|
+
export function AuthControls() {
|
|
85
|
+
const { isLoaded, isSignedIn, signOut } = useAuth();
|
|
86
|
+
const { startSSOFlow } = useSSO();
|
|
87
|
+
const [error, setError] = useState<string | null>(null);
|
|
88
|
+
const [pending, setPending] = useState(false);
|
|
89
|
+
async function authenticate() {
|
|
90
|
+
setPending(true); setError(null);
|
|
91
|
+
try {
|
|
92
|
+
const { createdSessionId, setActive } = await startSSOFlow({
|
|
93
|
+
strategy: 'oauth_google', redirectUrl: AuthSession.makeRedirectUri({ scheme: 'ccm-${ctx.options.name}-${app.name}', path: 'continue' }),
|
|
94
|
+
});
|
|
95
|
+
if (createdSessionId && setActive) await setActive({ session: createdSessionId });
|
|
96
|
+
else setError('Sign-in was cancelled or requires additional verification.');
|
|
97
|
+
} catch (cause) { setError(cause instanceof Error ? cause.message : 'Sign-in failed.'); }
|
|
98
|
+
finally { setPending(false); }
|
|
99
|
+
}
|
|
100
|
+
async function logout() {
|
|
101
|
+
try { await signOut(); } catch { setError('Could not sign out. Try again.'); }
|
|
102
|
+
}
|
|
103
|
+
if (!isLoaded) return <Text>Loading sign-in…</Text>;
|
|
104
|
+
return <View>
|
|
105
|
+
{isSignedIn ? <Button title="Sign out" onPress={() => { void logout(); }} /> : <Button title={pending ? 'Signing in…' : 'Sign in with Google'} disabled={pending} onPress={() => { void authenticate(); }} />}
|
|
106
|
+
{error && <Text accessibilityRole="alert">{error}</Text>}
|
|
107
|
+
</View>;
|
|
108
|
+
}
|
|
109
|
+
`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/integrations/auth/clerk/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,QAAQ,GAAwD;IACpE,IAAI,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE;IACpD,IAAI,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,UAAU,EAAE;IACpD,gBAAgB,EAAE,EAAE,GAAG,EAAE,6BAA6B,EAAE,OAAO,EAAE,QAAQ,EAAE;IAC3E,IAAI,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE;CACnD,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAgB;IACvC,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,OAAO;IACd,KAAK,CAAC,KAAK,CAAC,GAAG;QACb,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU;YACpC,MAAM,GAAG,CAAC,KAAK,CACb,mCAAmC,EACnC;;;;;;CAMP,CACM,CAAC;QACJ,MAAM,GAAG,CAAC,KAAK,CACb,wCAAwC,EACxC;;;;CAIL,CACI,CAAC;QACF,MAAM,GAAG,CAAC,KAAK,CACb,qCAAqC,EACrC,iKAAiK,CAClK,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACxC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YACzC,MAAM,GAAG,CAAC,YAAY,CAAC,GAAG,GAAG,eAAe,EAAE;gBAC5C,YAAY,EAAE;oBACZ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO;oBAC9B,GAAG,CAAC,MAAM;wBACR,CAAC,CAAC;4BACE,mBAAmB,EAAE,QAAQ;4BAC7B,mBAAmB,EAAE,SAAS;4BAC9B,kBAAkB,EAAE,QAAQ;4BAC5B,aAAa,EAAE,QAAQ;4BACvB,gBAAgB,EAAE,SAAS;yBAC5B;wBACH,CAAC,CAAC,EAAE,CAAC;iBACR;aACF,CAAC,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,CACb,GAAG,GAAG,qBAAqB,EAC3B,kEAAkE,MAAM,4BAA4B,GAAG,CAAC,SAAS,KAAK,MAAM,IAAI,GAAG,CAAC,SAAS,KAAK,gBAAgB,CAAC,CAAC,CAAC,iGAAiG,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,gFAAgF,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,4FAA4F,CAAC,CAAC,CAAC,EAAE,EAAE,CACve,CAAC;YACF,MAAM,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC7B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,GAAG,CAAC,MAAM;oBACR,CAAC,CAAC;wBACE,YAAY,EACV,uDAAuD;wBACzD,aAAa,EAAE,yBAAyB;qBACzC;oBACH,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;gBAC7B,MAAM,GAAG,CAAC,KAAK,CACb,GAAG,GAAG,eAAe,EACrB;;;CAGT,CACQ,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,CAAC,SAAS,KAAK,gBAAgB,EAAE,CAAC;gBACvC,MAAM,GAAG,CAAC,KAAK,CACb,GAAG,GAAG,eAAe,EACrB;;;CAGT,CACQ,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,CAAC,KAAK,CACb,GAAG,GAAG,wBAAwB,EAC9B;qDAC2C,OAAO,CAAC,GAAG;;;;;;CAM/D,CACQ,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC,KAAK,CACb,GAAG,GAAG,wBAAwB,EAC9B;;;;;;;;;;;;;;;4FAekF,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI;;;;;;;;;;;;;;;;CAgBvH,CACQ,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,MAAM,CAAC,MAAM,YAAY,GAA8B;IACrD,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,YAAY;CACpB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { writeProviders } from '../shared.js';
|
|
2
|
+
export const noneAdapter = {
|
|
3
|
+
id: 'none',
|
|
4
|
+
label: 'None',
|
|
5
|
+
async apply(ctx) {
|
|
6
|
+
if (ctx.options.example === 'messages')
|
|
7
|
+
await ctx.write('packages/backend/convex/access.ts', `import type { QueryCtx, MutationCtx } from './_generated/server';
|
|
8
|
+
export async function getOwner(_ctx: QueryCtx | MutationCtx): Promise<string | undefined> {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
`);
|
|
12
|
+
for (const app of ctx.options.apps) {
|
|
13
|
+
await writeProviders(ctx, app);
|
|
14
|
+
await ctx.write(`apps/${app.name}/src/auth-controls.tsx`, 'export function AuthControls() { return null; }\n');
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/integrations/auth/none/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,MAAM;IACb,KAAK,CAAC,KAAK,CAAC,GAAG;QACb,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU;YACpC,MAAM,GAAG,CAAC,KAAK,CACb,mCAAmC,EACnC;;;;CAIP,CACM,CAAC;QACJ,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC/B,MAAM,GAAG,CAAC,KAAK,CACb,QAAQ,GAAG,CAAC,IAAI,wBAAwB,EACxC,mDAAmD,CACpD,CAAC;QACJ,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppSpec, GeneratorContext } from '../../generator/types.js';
|
|
2
|
+
export declare function platform(app: AppSpec): {
|
|
3
|
+
native: boolean;
|
|
4
|
+
prefix: string;
|
|
5
|
+
env: (name: string) => string;
|
|
6
|
+
};
|
|
7
|
+
export declare function writeProviders(ctx: GeneratorContext, app: AppSpec, auth?: {
|
|
8
|
+
sdk: string;
|
|
9
|
+
extraImports?: string;
|
|
10
|
+
providerProps?: string;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function platform(app) {
|
|
2
|
+
const native = app.framework === 'expo';
|
|
3
|
+
const prefix = app.framework === 'next' ? 'NEXT_PUBLIC' : native ? 'EXPO_PUBLIC' : 'VITE';
|
|
4
|
+
const env = (name) => `${prefix === 'VITE' ? 'import.meta.env' : 'process.env'}.${prefix}_${name}`;
|
|
5
|
+
return { native, prefix, env };
|
|
6
|
+
}
|
|
7
|
+
export async function writeProviders(ctx, app, auth) {
|
|
8
|
+
const { native, env } = platform(app);
|
|
9
|
+
const text = (message) => native ? `<Text>${message}</Text>` : `<p>${message}</p>`;
|
|
10
|
+
await ctx.write(`apps/${app.name}/src/providers.tsx`, `'use client';
|
|
11
|
+
import { useState, type ReactNode } from 'react';
|
|
12
|
+
import { ConvexReactClient, ${auth ? 'Authenticated, Unauthenticated, AuthLoading' : 'ConvexProvider'} } from 'convex/react';
|
|
13
|
+
${native ? "import { Text, View } from 'react-native';" : ''}
|
|
14
|
+
${auth
|
|
15
|
+
? `import { ClerkProvider, useAuth } from '${auth.sdk}';
|
|
16
|
+
import { ConvexProviderWithClerk } from 'convex/react-clerk';
|
|
17
|
+
import { AuthControls } from './auth-controls';
|
|
18
|
+
${auth.extraImports ?? ''}`
|
|
19
|
+
: ''}
|
|
20
|
+
|
|
21
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
22
|
+
const url = ${env('CONVEX_URL')};
|
|
23
|
+
${auth ? `const publishableKey = ${env('CLERK_PUBLISHABLE_KEY')};` : ''}
|
|
24
|
+
if (!url) return ${text(`Set ${platform(app).prefix}_CONVEX_URL in this app's .env.local.`)};
|
|
25
|
+
${auth ? `if (!publishableKey) return ${text(`Set ${platform(app).prefix}_CLERK_PUBLISHABLE_KEY in this app's .env.local.`)};` : ''}
|
|
26
|
+
return ${auth ? `<ClerkProvider publishableKey={publishableKey} ${auth.providerProps ?? ''}>` : ''}<Connection url={url}>{children}</Connection>${auth ? '</ClerkProvider>' : ''};
|
|
27
|
+
}
|
|
28
|
+
function Connection({ url, children }: { url: string; children: ReactNode }) {
|
|
29
|
+
const [client] = useState(() => new ConvexReactClient(url${native ? ', { unsavedChangesWarning: false }' : ''}));
|
|
30
|
+
return ${auth
|
|
31
|
+
? `<ConvexProviderWithClerk client={client} useAuth={useAuth}>
|
|
32
|
+
<AuthLoading>${text('Connecting authentication…')}</AuthLoading>
|
|
33
|
+
<Unauthenticated><AuthControls /></Unauthenticated>
|
|
34
|
+
<Authenticated>${native ? '<View style={{ flex: 1, padding: 24, paddingTop: 64 }}>' : ''}{children}${native ? '</View>' : ''}</Authenticated>
|
|
35
|
+
</ConvexProviderWithClerk>`
|
|
36
|
+
: `<ConvexProvider client={client}>${native ? '<View style={{ flex: 1, padding: 24, paddingTop: 64 }}>' : ''}{children}${native ? '</View>' : ''}</ConvexProvider>`};
|
|
37
|
+
}
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/integrations/auth/shared.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC;IACxC,MAAM,MAAM,GACV,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7E,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAC3B,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;IAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAqB,EACrB,GAAY,EACZ,IAIC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE,CAC/B,MAAM,CAAC,CAAC,CAAC,SAAS,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC;IAC3D,MAAM,GAAG,CAAC,KAAK,CACb,QAAQ,GAAG,CAAC,IAAI,oBAAoB,EACpC;;8BAE0B,IAAI,CAAC,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC,gBAAgB;EACnG,MAAM,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,EAAE;EAE1D,IAAI;QACF,CAAC,CAAC,2CAA2C,IAAI,CAAC,GAAG;;;EAGvD,IAAI,CAAC,YAAY,IAAI,EAAE,EAAE;QACvB,CAAC,CAAC,EACN;;;gBAGgB,GAAG,CAAC,YAAY,CAAC;IAC7B,IAAI,CAAC,CAAC,CAAC,0BAA0B,GAAG,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;qBACpD,IAAI,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,uCAAuC,CAAC;IACzF,IAAI,CAAC,CAAC,CAAC,+BAA+B,IAAI,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,kDAAkD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;WAC1H,IAAI,CAAC,CAAC,CAAC,kDAAkD,IAAI,CAAC,aAAa,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,gDAAgD,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE;;;6DAGrH,MAAM,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,EAAE;WAE3G,IAAI;QACF,CAAC,CAAC;mBACW,IAAI,CAAC,4BAA4B,CAAC;;qBAEhC,MAAM,CAAC,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC,EAAE,aAAa,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;6BACnG;QACvB,CAAC,CAAC,mCAAmC,MAAM,CAAC,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC,EAAE,aAAa,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,mBACpJ;;CAED,CACE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function runCommand(command: string, args: readonly string[], cwd: string, signal?: AbortSignal): Promise<void>;
|
|
2
|
+
export interface PackageManager {
|
|
3
|
+
id: 'pnpm';
|
|
4
|
+
install(cwd: string, signal?: AbortSignal): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare const pnpm: PackageManager;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import spawn from 'cross-spawn';
|
|
2
|
+
export function runCommand(command, args, cwd, signal) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const child = spawn(command, [...args], {
|
|
5
|
+
cwd,
|
|
6
|
+
stdio: 'inherit',
|
|
7
|
+
shell: false,
|
|
8
|
+
...(signal ? { signal } : {}),
|
|
9
|
+
});
|
|
10
|
+
child.once('error', (error) => reject(new Error(`Could not run ${command}: ${error.message}`, {
|
|
11
|
+
cause: error,
|
|
12
|
+
})));
|
|
13
|
+
child.once('exit', (code, signal) => code === 0
|
|
14
|
+
? resolve()
|
|
15
|
+
: reject(new Error(`${command} ${args.join(' ')} failed (${signal ?? `exit ${code}`}).`)));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export const pnpm = {
|
|
19
|
+
id: 'pnpm',
|
|
20
|
+
install: (cwd, signal) => runCommand('pnpm', ['install'], cwd, signal),
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/package-manager/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,aAAa,CAAC;AAEhC,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,IAAuB,EACvB,GAAW,EACX,MAAoB;IAEpB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE;YACtC,GAAG;YACH,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,KAAK;YACZ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAC5B,MAAM,CACJ,IAAI,KAAK,CAAC,iBAAiB,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE;YACtD,KAAK,EAAE,KAAK;SACb,CAAC,CACH,CACF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAClC,IAAI,KAAK,CAAC;YACR,CAAC,CAAC,OAAO,EAAE;YACX,CAAC,CAAC,MAAM,CACJ,IAAI,KAAK,CACP,GAAG,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,MAAM,IAAI,QAAQ,IAAI,EAAE,IAAI,CACrE,CACF,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAKD,MAAM,CAAC,MAAM,IAAI,GAAmB;IAClC,EAAE,EAAE,MAAM;IACV,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC;CACvE,CAAC"}
|