- moves workspace:* dependencies to dev-dependencies to avoid spreading them in npm releases - remove fix on rollup.external - remove prepublishOnly and postpublish scripts - set bundle packages to private - add release-dump-version that update package.json version before releasing to npm - add release-verify-build that check no externalized twenty package exists in `dist` before releasing to npm - works with new release github action here -> https://github.com/twentyhq/twenty-infra/pull/397
287 lines
6.4 KiB
TypeScript
287 lines
6.4 KiB
TypeScript
import * as fs from 'fs-extra';
|
|
import { join } from 'path';
|
|
import { v4 } from 'uuid';
|
|
import { ASSETS_DIR } from 'twenty-shared/application';
|
|
|
|
const SRC_FOLDER = 'src';
|
|
|
|
export const copyBaseApplicationProject = async ({
|
|
appName,
|
|
appDisplayName,
|
|
appDescription,
|
|
appDirectory,
|
|
}: {
|
|
appName: string;
|
|
appDisplayName: string;
|
|
appDescription: string;
|
|
appDirectory: string;
|
|
}) => {
|
|
await fs.copy(join(__dirname, './constants/base-application'), appDirectory);
|
|
|
|
await createPackageJson({ appName, appDirectory });
|
|
|
|
await createGitignore(appDirectory);
|
|
|
|
await createPublicAssetDirectory(appDirectory);
|
|
|
|
await createYarnLock(appDirectory);
|
|
|
|
const sourceFolderPath = join(appDirectory, SRC_FOLDER);
|
|
|
|
await fs.ensureDir(sourceFolderPath);
|
|
|
|
await createDefaultRoleConfig({
|
|
displayName: appDisplayName,
|
|
appDirectory: sourceFolderPath,
|
|
fileFolder: 'roles',
|
|
fileName: 'default-role.ts',
|
|
});
|
|
|
|
await createDefaultFrontComponent({
|
|
appDirectory: sourceFolderPath,
|
|
fileFolder: 'front-components',
|
|
fileName: 'hello-world.tsx',
|
|
});
|
|
|
|
await createDefaultFunction({
|
|
appDirectory: sourceFolderPath,
|
|
fileFolder: 'logic-functions',
|
|
fileName: 'hello-world.ts',
|
|
});
|
|
|
|
await createApplicationConfig({
|
|
displayName: appDisplayName,
|
|
description: appDescription,
|
|
appDirectory: sourceFolderPath,
|
|
fileName: 'application-config.ts',
|
|
});
|
|
};
|
|
|
|
const createPublicAssetDirectory = async (appDirectory: string) => {
|
|
await fs.ensureDir(join(appDirectory, ASSETS_DIR));
|
|
};
|
|
|
|
const createYarnLock = async (appDirectory: string) => {
|
|
const yarnLockContent = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
# yarn lockfile v1
|
|
`;
|
|
|
|
await fs.writeFile(join(appDirectory, 'yarn.lock'), yarnLockContent);
|
|
};
|
|
const createGitignore = async (appDirectory: string) => {
|
|
const gitignoreContent = `# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
|
|
# dependencies
|
|
/node_modules
|
|
/.pnp
|
|
.pnp.*
|
|
.yarn
|
|
|
|
# codegen
|
|
generated
|
|
|
|
# testing
|
|
/coverage
|
|
|
|
# dev
|
|
/dist/
|
|
|
|
.twenty/*
|
|
!.twenty/output/
|
|
|
|
# production
|
|
/build
|
|
|
|
# misc
|
|
.DS_Store
|
|
*.pem
|
|
|
|
# debug
|
|
npm-debug.log*
|
|
yarn-debug.log*
|
|
yarn-error.log*
|
|
.pnpm-debug.log*
|
|
|
|
# env files (can opt-in for committing if needed)
|
|
.env*
|
|
|
|
# typescript
|
|
*.tsbuildinfo
|
|
`;
|
|
|
|
await fs.writeFile(join(appDirectory, '.gitignore'), gitignoreContent);
|
|
};
|
|
|
|
const createDefaultRoleConfig = async ({
|
|
displayName,
|
|
appDirectory,
|
|
fileFolder,
|
|
fileName,
|
|
}: {
|
|
displayName: string;
|
|
appDirectory: string;
|
|
fileFolder?: string;
|
|
fileName: string;
|
|
}) => {
|
|
const universalIdentifier = v4();
|
|
|
|
const content = `import { defineRole } from 'twenty-sdk';
|
|
|
|
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
|
'${universalIdentifier}';
|
|
|
|
export default defineRole({
|
|
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
|
label: '${displayName} default function role',
|
|
description: '${displayName} default function role',
|
|
canReadAllObjectRecords: true,
|
|
canUpdateAllObjectRecords: true,
|
|
canSoftDeleteAllObjectRecords: true,
|
|
canDestroyAllObjectRecords: false,
|
|
});
|
|
`;
|
|
|
|
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
|
|
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
|
};
|
|
|
|
const createDefaultFrontComponent = async ({
|
|
appDirectory,
|
|
fileFolder,
|
|
fileName,
|
|
}: {
|
|
appDirectory: string;
|
|
fileFolder?: string;
|
|
fileName: string;
|
|
}) => {
|
|
const universalIdentifier = v4();
|
|
|
|
const content = `import { defineFrontComponent } from 'twenty-sdk';
|
|
|
|
export const HelloWorld = () => {
|
|
return (
|
|
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
|
<h1>Hello, World!</h1>
|
|
<p>This is your first front component.</p>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default defineFrontComponent({
|
|
universalIdentifier: '${universalIdentifier}',
|
|
name: 'hello-world-front-component',
|
|
description: 'A sample front component',
|
|
component: HelloWorld,
|
|
});
|
|
`;
|
|
|
|
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
|
|
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
|
};
|
|
|
|
const createDefaultFunction = async ({
|
|
appDirectory,
|
|
fileFolder,
|
|
fileName,
|
|
}: {
|
|
appDirectory: string;
|
|
fileFolder?: string;
|
|
fileName: string;
|
|
}) => {
|
|
const universalIdentifier = v4();
|
|
|
|
const content = `import { defineLogicFunction } from 'twenty-sdk';
|
|
|
|
const handler = async (): Promise<{ message: string }> => {
|
|
return { message: 'Hello, World!' };
|
|
};
|
|
|
|
// Logic function handler - rename and implement your logic
|
|
export default defineLogicFunction({
|
|
universalIdentifier: '${universalIdentifier}',
|
|
name: 'hello-world-logic-function',
|
|
description: 'A simple logic function',
|
|
timeoutSeconds: 5,
|
|
handler,
|
|
httpRouteTriggerSettings: {
|
|
path: '/hello-world-logic-function',
|
|
httpMethod: 'GET',
|
|
isAuthRequired: false,
|
|
},
|
|
});
|
|
`;
|
|
|
|
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
|
|
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
|
};
|
|
|
|
const createApplicationConfig = async ({
|
|
displayName,
|
|
description,
|
|
appDirectory,
|
|
fileFolder,
|
|
fileName,
|
|
}: {
|
|
displayName: string;
|
|
description?: string;
|
|
appDirectory: string;
|
|
fileFolder?: string;
|
|
fileName: string;
|
|
}) => {
|
|
const content = `import { defineApplication } from 'twenty-sdk';
|
|
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
|
|
|
export default defineApplication({
|
|
universalIdentifier: '${v4()}',
|
|
displayName: '${displayName}',
|
|
description: '${description ?? ''}',
|
|
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
|
});
|
|
`;
|
|
|
|
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
|
|
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
|
};
|
|
|
|
const createPackageJson = async ({
|
|
appName,
|
|
appDirectory,
|
|
}: {
|
|
appName: string;
|
|
appDirectory: string;
|
|
}) => {
|
|
const packageJson = {
|
|
name: appName,
|
|
version: '0.1.0',
|
|
license: 'MIT',
|
|
engines: {
|
|
node: '^24.5.0',
|
|
npm: 'please-use-yarn',
|
|
yarn: '>=4.0.2',
|
|
},
|
|
packageManager: 'yarn@4.9.2',
|
|
scripts: {
|
|
twenty: 'twenty',
|
|
lint: 'eslint',
|
|
'lint:fix': 'eslint --fix',
|
|
},
|
|
dependencies: {
|
|
'twenty-sdk': 'latest',
|
|
},
|
|
devDependencies: {
|
|
typescript: '^5.9.3',
|
|
'@types/node': '^24.7.2',
|
|
'@types/react': '^18.2.0',
|
|
react: '^18.2.0',
|
|
eslint: '^9.32.0',
|
|
'typescript-eslint': '^8.50.0',
|
|
},
|
|
};
|
|
|
|
await fs.writeFile(
|
|
join(appDirectory, 'package.json'),
|
|
JSON.stringify(packageJson, null, 2),
|
|
'utf8',
|
|
);
|
|
};
|