fix(create-twenty-app): preserve .yarnrc.yml in template (#20623)
**Source:** https://sonarly.com/issue/37981?type=bug ## Summary New apps created with `create-twenty-app@2.5.0` can fail at `yarn twenty dev` with `Could not resolve "twenty-sdk/define"`, blocking onboarding for app developers. ## Root cause Proximate cause: manifest module loading in the SDK fails to resolve `twenty-sdk/define` when the generated app uses Yarn PnP (no `node_modules` tree), and esbuild is invoked with normal Node-style resolution. - The failing path is `extractManifestFromFile()` → `loadModule()` in `packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-extract-config-from-file.ts`, which calls `esbuild.build({ bundle: true, ... })` and does not stub/mock `twenty-sdk/define` [ref: read `manifest-extract-config-from-file.ts`]. - The scaffolded template imports `twenty-sdk/define` in `src/application-config.ts` and `src/default-role.ts` [ref: grep in `packages/create-twenty-app/src/constants/template/src/*`]. - If esbuild cannot resolve that import from the app environment, the exact error matches the issue: `src/application-config.ts:1:34: ERROR: Could not resolve "twenty-sdk/define"`. Triggering cause (why now): `create-twenty-app@2.5.0` (the current npm `latest`) ships a template tarball without `.yarnrc.yml`, so new projects silently default to Yarn PnP instead of `node-modules`. Evidence: - Source template contains `.yarnrc.yml` with `nodeLinker: node-modules` [ref: read `packages/create-twenty-app/src/constants/template/.yarnrc.yml`]. - Published npm tarball for `create-twenty-app-2.5.0.tgz` does **not** contain `template/.yarnrc.yml` (but does contain renamed `gitignore`/`github`) [ref: tarball listing command output: `hasYarnrc: false`]. - Dotfile-preservation logic in `copyBaseApplicationProject()` only renames `gitignore` and `github`; it does not preserve `.yarnrc.yml` [ref: read `packages/create-twenty-app/src/utils/app-template.ts`]. - `npm dist-tags` shows `latest: 2.5.0`, so users following docs with `@latest` receive this broken scaffold path now [ref: npm registry query]. Why this is attributable to a specific change: - Commit `15eb3e7edccdf4e9770a00a07bfbd026420f7c3b` introduced dotfile-preservation mechanics for template publish (`gitignore`/`github`) but left out `.yarnrc.yml`, creating the regression window for newly scaffolded apps [ref: `git show --stat 15eb3e7...`, `git blame` on `renameDotfiles()`]. ## Fix Implemented a targeted fix in `create-twenty-app` so `.yarnrc.yml` is preserved through npm packaging the same way `.gitignore` and `.github` are handled. What changed: 1) Template dotfile preservation - Removed `packages/create-twenty-app/src/constants/template/.yarnrc.yml` - Added `packages/create-twenty-app/src/constants/template/yarnrc.yml` with identical content: - `nodeLinker: node-modules` This avoids npm stripping the file from the published tarball. 2) Scaffold rename logic - Updated `packages/create-twenty-app/src/utils/app-template.ts`: - Added `{ from: 'yarnrc.yml', to: '.yarnrc.yml' }` in `renameDotfiles()` - Updated progress text and inline comment to include `.yarnrc.yml` So generated apps reliably restore `.yarnrc.yml` after template copy. 3) Regression test - Updated `packages/create-twenty-app/src/utils/__tests__/app-template.spec.ts`: - Added a test asserting `yarnrc.yml` is renamed to `.yarnrc.yml` - Added a small constant for the test path This locks the behavior and prevents reintroducing the publish omission regression. Validation notes: - Attempted to run the focused Jest test, but execution failed due missing workspace dependency state (`@nx/jest/preset` / node_modules state not installed in this environment). ## Original request fix(create-twenty-app): preserve .yarnrc.yml in template _Created by Sonarly by autonomous analysis (run 43375)._ --------- Co-authored-by: sonarly-bot <sonarly@sonarly.com> Co-authored-by: martmull <martmull@hotmail.fr>
This commit is contained in:
@@ -17,6 +17,7 @@ const UNIVERSAL_IDENTIFIERS_PATH = join(
|
||||
'constants',
|
||||
'universal-identifiers.ts',
|
||||
);
|
||||
const YARNRC_PATH = 'yarnrc.yml';
|
||||
|
||||
// Template content matching template/src/constants/universal-identifiers.ts
|
||||
const TEMPLATE_UNIVERSAL_IDENTIFIERS = `export const APP_DISPLAY_NAME = 'DISPLAY-NAME-TO-BE-GENERATED';
|
||||
@@ -173,6 +174,27 @@ describe('copyBaseApplicationProject', () => {
|
||||
expect(publicDirectoryContents).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should rename yarnrc.yml to .yarnrc.yml in the scaffolded project', async () => {
|
||||
await fs.writeFile(
|
||||
join(testAppDirectory, YARNRC_PATH),
|
||||
'nodeLinker: node-modules',
|
||||
);
|
||||
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
appDisplayName: 'My Test App',
|
||||
appDescription: 'A test application',
|
||||
appDirectory: testAppDirectory,
|
||||
});
|
||||
|
||||
expect(await fs.pathExists(join(testAppDirectory, YARNRC_PATH))).toBe(
|
||||
false,
|
||||
);
|
||||
expect(await fs.pathExists(join(testAppDirectory, '.yarnrc.yml'))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty description', async () => {
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
|
||||
@@ -22,7 +22,7 @@ export const copyBaseApplicationProject = async ({
|
||||
onProgress?.('Copying base template');
|
||||
await fs.copy(join(__dirname, './constants/template'), appDirectory);
|
||||
|
||||
onProgress?.('Configuring dotfiles (.gitignore, .github)');
|
||||
onProgress?.('Configuring dotfiles (.gitignore, .github, .yarnrc.yml)');
|
||||
await renameDotfiles({ appDirectory });
|
||||
|
||||
onProgress?.('Mirroring AGENTS.md to CLAUDE.md');
|
||||
@@ -47,6 +47,7 @@ const renameDotfiles = async ({ appDirectory }: { appDirectory: string }) => {
|
||||
const renames = [
|
||||
{ from: 'gitignore', to: '.gitignore' },
|
||||
{ from: 'github', to: '.github' },
|
||||
{ from: 'yarnrc.yml', to: '.yarnrc.yml' },
|
||||
];
|
||||
|
||||
for (const { from, to } of renames) {
|
||||
|
||||
Reference in New Issue
Block a user