Skip to content

Commit 9df93d0

Browse files
nicktrnmatt-aitken
andauthored
CLI create-integration templates and shared configs (#511)
* Add tsup config package * Add integration tsconfig to extend from * Return correct version via cli -v * Make create-integration use templates * Add changeset * Switch to liquidjs templates --------- Co-authored-by: Matt Aitken <[email protected]>
1 parent 6769d6b commit 9df93d0

25 files changed

+1369
-98
lines changed

.changeset/gorgeous-panthers-run.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/cli": patch
3+
---
4+
5+
Improve create-integration output. Use templates and shared configs.

config-packages/tsconfig/integration.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"compilerOptions": {
44
"lib": ["DOM", "DOM.Iterable", "ES2019"],
55
"paths": {
6+
"@trigger.dev/tsup/*": ["../../config-packages/tsup/src/*"],
7+
"@trigger.dev/tsup": ["../../config-packages/tsup/src/index"],
68
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
79
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
810
"@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"],

config-packages/tsup/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@trigger.dev/tsup",
3+
"version": "0.0.0",
4+
"private": true,
5+
"license": "MIT",
6+
"devDependencies": {
7+
"tsup": "7.1.x"
8+
}
9+
}

config-packages/tsup/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { defineConfig } from "tsup";
2+
export { deepMergeOptions } from "./utils";
3+
export { options as integrationOptions } from "./integration";
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Options, defineConfig } from "tsup";
2+
3+
export const options: Options = {
4+
name: "main",
5+
entry: ["./src/index.ts"],
6+
outDir: "./dist",
7+
platform: "node",
8+
format: ["cjs"],
9+
legacyOutput: true,
10+
sourcemap: true,
11+
clean: true,
12+
bundle: true,
13+
splitting: false,
14+
dts: true,
15+
treeshake: {
16+
preset: "smallest",
17+
},
18+
esbuildPlugins: [],
19+
external: ["http", "https", "util", "events", "tty", "os", "timers"],
20+
};
21+
22+
export default defineConfig(options);

config-packages/tsup/src/utils.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Options } from "tsup";
2+
3+
export const deepMergeOptions = deepMergeRecords<Options>;
4+
5+
function deepMergeRecords<TRecord extends Record<any, any>>(...options: TRecord[]): TRecord {
6+
const result = {} as TRecord;
7+
8+
for (const option of options) {
9+
for (const key in option) {
10+
if (option.hasOwnProperty(key)) {
11+
const optionValue = option[key];
12+
const existingValue = result[key];
13+
14+
if (
15+
existingValue &&
16+
typeof existingValue === "object" &&
17+
typeof optionValue === "object" &&
18+
!Array.isArray(existingValue) &&
19+
!Array.isArray(optionValue) &&
20+
existingValue !== null &&
21+
optionValue !== null
22+
) {
23+
result[key] = deepMergeRecords(existingValue, optionValue);
24+
} else {
25+
result[key] = optionValue;
26+
}
27+
}
28+
}
29+
}
30+
31+
return result;
32+
}

packages/cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"execa": "^7.0.0",
6969
"gradient-string": "^2.0.2",
7070
"inquirer": "^9.1.4",
71+
"liquidjs": "^10.9.2",
7172
"localtunnel": "^2.0.2",
7273
"mock-fs": "^5.2.0",
7374
"nanoid": "^4.0.2",

packages/cli/src/cli/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import { checkApiKeyIsDevServer } from "../utils/getApiKeyType";
1414

1515
export const program = new Command();
1616

17-
program.name(COMMAND_NAME).description("The Trigger.dev CLI").version("0.0.1");
17+
program
18+
.name(COMMAND_NAME)
19+
.description("The Trigger.dev CLI")
20+
.version(getVersion(), "-v, --version", "Display the version number");
1821

1922
program
2023
.command("init")

packages/cli/src/commands/createIntegration.ts

Lines changed: 123 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { generateIntegrationFiles } from "../utils/generateIntegrationFiles";
99
import { getPackageName } from "../utils/getPackagName";
1010
import { installDependencies } from "../utils/installDependencies";
1111
import { logger } from "../utils/logger";
12-
import { resolvePath } from "../utils/parseNameAndPath";
12+
import { relativePath, resolvePath } from "../utils/parseNameAndPath";
13+
import { createIntegrationFileFromTemplate } from "../utils/createIntegrationFileFromTemplate";
1314

1415
const CLIOptionsSchema = z.object({
1516
packageName: z.string().optional(),
@@ -91,108 +92,107 @@ export async function createIntegrationCommand(path: string, cliOptions: any) {
9192
process.exit(1);
9293
}
9394

94-
// Create the package.json
95-
const packageJson = {
96-
name: resolvedOptions.packageName,
97-
version: "0.0.1",
98-
description: `Trigger.dev integration for ${resolvedOptions.sdkPackage}`,
99-
main: "./dist/index.js",
100-
types: "./dist/index.d.ts",
101-
publishConfig: {
102-
access: "public",
103-
},
104-
files: ["dist/index.js", "dist/index.d.ts", "dist/index.js.map"],
105-
devDependencies: {
106-
"@types/node": "16.x",
107-
rimraf: "^3.0.2",
108-
tsup: "7.1.x",
109-
typescript: "4.9.4",
110-
},
111-
scripts: {
112-
clean: "rimraf dist",
113-
build: "npm run clean && npm run build:tsup",
114-
"build:tsup": "tsup",
115-
typecheck: "tsc --noEmit",
116-
},
117-
dependencies: {
118-
[latestVersion.name]: `^${latestVersion.version}`,
119-
[sdkVersion.name]: sdkVersion.version,
120-
[integrationKitVersion.name]: integrationKitVersion.version,
121-
},
122-
engines: {
123-
node: ">=16.8.0",
124-
},
125-
};
95+
const integrationVersion = await getInternalOrExternalPackageVersion({
96+
path: "integrations/github",
97+
packageName: "@trigger.dev/github",
98+
tag: "latest",
99+
monorepoPath: triggerMonorepoPath,
100+
prependWorkspace: false,
101+
});
102+
103+
if (!integrationVersion) {
104+
logger.error(
105+
`Could not find the latest version of @trigger.dev/github. Please try again later.`
106+
);
126107

127-
await createFileInPath(resolvedPath, "package.json", JSON.stringify(packageJson, null, 2));
108+
process.exit(1);
109+
}
128110

129-
// Create the tsconfig.json
130-
const tsconfigJson = {
131-
compilerOptions: {
132-
composite: false,
133-
declaration: false,
134-
declarationMap: false,
135-
esModuleInterop: true,
136-
forceConsistentCasingInFileNames: true,
137-
inlineSources: false,
138-
isolatedModules: true,
139-
moduleResolution: "node16",
140-
noUnusedLocals: false,
141-
noUnusedParameters: false,
142-
preserveWatchOutput: true,
143-
skipLibCheck: true,
144-
strict: true,
145-
experimentalDecorators: true,
146-
emitDecoratorMetadata: true,
147-
sourceMap: true,
148-
resolveJsonModule: true,
149-
lib: ["es2019"],
150-
module: "commonjs",
151-
target: "es2021",
152-
},
153-
include: ["./src/**/*.ts", "tsup.config.ts"],
154-
exclude: ["node_modules"],
111+
const baseVariables = {
112+
packageName: resolvedOptions.packageName,
113+
sdkPackage: resolvedOptions.sdkPackage,
114+
integrationVersion: integrationVersion,
115+
latestVersion: latestVersion,
116+
sdkVersion: sdkVersion,
117+
integrationKitVersion: integrationKitVersion,
118+
triggerMonorepoPath,
155119
};
156120

157-
await createFileInPath(resolvedPath, "tsconfig.json", JSON.stringify(tsconfigJson, null, 2));
158-
159-
const readme = `
160-
# ${resolvedOptions.packageName}
161-
`;
162-
163-
await createFileInPath(resolvedPath, "README.md", readme);
164-
165-
// Create the tsup.config.ts
166-
const tsupConfig = `
167-
import { defineConfig } from "tsup";
168-
169-
export default defineConfig([
170-
{
171-
name: "main",
172-
entry: ["./src/index.ts"],
173-
outDir: "./dist",
174-
platform: "node",
175-
format: ["cjs"],
176-
legacyOutput: true,
177-
sourcemap: true,
178-
clean: true,
179-
bundle: true,
180-
splitting: false,
181-
dts: true,
182-
treeshake: {
183-
preset: "smallest",
184-
},
185-
esbuildPlugins: [],
186-
external: ["http", "https", "util", "events", "tty", "os", "timers"],
187-
},
188-
]);
121+
const getOutputPath = (relativePath: string) => pathModule.join(resolvedPath, relativePath);
189122

190-
`;
123+
const miscIntegrationFiles = [
124+
{
125+
relativeTemplatePath: "package.json.j2",
126+
outputPath: getOutputPath("package.json"),
127+
},
128+
{
129+
// use `tsc --showConfig` to update external tsconfig
130+
relativeTemplatePath: `tsconfig-${triggerMonorepoPath ? "internal" : "external"}.json.j2`,
131+
outputPath: getOutputPath("tsconfig.json"),
132+
},
133+
{
134+
relativeTemplatePath: `tsup.config-${triggerMonorepoPath ? "internal" : "external"}.js.j2`,
135+
outputPath: getOutputPath("tsup.config.ts"),
136+
},
137+
{
138+
relativeTemplatePath: "README.md.j2",
139+
outputPath: getOutputPath("README.md"),
140+
},
141+
];
191142

192-
await createFileInPath(resolvedPath, "tsup.config.ts", tsupConfig);
143+
await createIntegrationFiles(miscIntegrationFiles, baseVariables);
193144

145+
// create src/*
194146
if (resolvedOptions.skipGeneratingCode) {
195-
await createFileInPath(resolvedPath, "src/index.ts", "export {}");
147+
const getSrcOutputPath = (relativePath: string) =>
148+
getOutputPath(pathModule.join("src", relativePath));
149+
150+
const srcIntegrationFiles = [
151+
{
152+
relativeTemplatePath: pathModule.join("payload-examples", "index.js.j2"),
153+
outputPath: getSrcOutputPath(pathModule.join("payload-examples", "index.ts")),
154+
},
155+
{
156+
relativeTemplatePath: "events.js.j2",
157+
outputPath: getSrcOutputPath("events.ts"),
158+
},
159+
{
160+
relativeTemplatePath: "index.js.j2",
161+
outputPath: getSrcOutputPath("index.ts"),
162+
},
163+
{
164+
relativeTemplatePath: "models.js.j2",
165+
outputPath: getSrcOutputPath("models.ts"),
166+
},
167+
{
168+
relativeTemplatePath: "schemas.js.j2",
169+
outputPath: getSrcOutputPath("schemas.ts"),
170+
},
171+
{
172+
relativeTemplatePath: "types.js.j2",
173+
outputPath: getSrcOutputPath("types.ts"),
174+
},
175+
{
176+
relativeTemplatePath: "utils.js.j2",
177+
outputPath: getSrcOutputPath("utils.ts"),
178+
},
179+
{
180+
relativeTemplatePath: "webhooks.js.j2",
181+
outputPath: getSrcOutputPath("webhooks.ts"),
182+
},
183+
];
184+
185+
const validIdentifier = pathModule
186+
.basename(path)
187+
.replace(/[^a-zA-Z0-9]+/g, "")
188+
.replace(/^[0-9]+/g, "");
189+
190+
await createIntegrationFiles(srcIntegrationFiles, {
191+
...baseVariables,
192+
apiKeyPropertyName: "apiKey", // TODO: prompt for this
193+
authMethod: resolvedOptions.authMethod,
194+
identifier: validIdentifier.length ? validIdentifier : "packageName",
195+
});
196196
} else {
197197
await attemptToGenerateIntegrationFiles(pathModule.join(resolvedPath, "src"), resolvedOptions);
198198
}
@@ -295,8 +295,9 @@ const resolveOptionsWithPrompts = async (
295295
resolvedOptions.skipGeneratingCode = true;
296296
}
297297

298+
resolvedOptions.authMethod = await promptAuthMethod();
299+
298300
if (!resolvedOptions.skipGeneratingCode) {
299-
resolvedOptions.authMethod = await promptAuthMethod();
300301
resolvedOptions.extraInfo = await promptExtraInfo();
301302
}
302303
} catch (err) {
@@ -448,11 +449,13 @@ async function getInternalOrExternalPackageVersion({
448449
tag,
449450
path,
450451
monorepoPath,
452+
prependWorkspace = true,
451453
}: {
452454
packageName: string;
453455
tag: string;
454456
path: string;
455457
monorepoPath?: string;
458+
prependWorkspace?: boolean;
456459
}): Promise<{ name: string; version: string } | undefined> {
457460
if (!monorepoPath) {
458461
return await getLatestPackageVersion(packageName, tag);
@@ -470,7 +473,7 @@ async function getInternalOrExternalPackageVersion({
470473

471474
return {
472475
name: packageJson.name,
473-
version: `workspace:^${packageJson.version}`,
476+
version: `${prependWorkspace ? "workspace:^" : ""}${packageJson.version}`,
474477
};
475478
}
476479

@@ -550,3 +553,26 @@ async function updateJobCatalogWithNewIntegration(
550553
};
551554
await writeJSONFile(tsConfigPath, newTsConfig);
552555
}
556+
557+
const createIntegrationFiles = async (
558+
files: {
559+
relativeTemplatePath: string;
560+
outputPath: string;
561+
}[],
562+
variables?: Record<string, any>
563+
) => {
564+
for (const file of files) {
565+
const result = await createIntegrationFileFromTemplate({ ...file, variables });
566+
handleCreateResult(file.outputPath, result);
567+
}
568+
};
569+
570+
const handleCreateResult = (
571+
outputPath: string,
572+
result: Awaited<ReturnType<typeof createIntegrationFileFromTemplate>>
573+
) => {
574+
if (!result.success) {
575+
throw new Error(`Failed to create ${pathModule.basename(outputPath)}: ${result.error}`);
576+
}
577+
logger.success(`✔ Created ${pathModule.basename(outputPath)} at ${relativePath(outputPath)}`);
578+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# {{ packageName }}

0 commit comments

Comments
 (0)