Skip to content

Commit

Permalink
add outDir param to the cli
Browse files Browse the repository at this point in the history
  • Loading branch information
shairez committed Nov 12, 2024
1 parent 9ecfa4c commit f4de901
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-pants-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik': minor
---

FEAT: add monorepo support to the `qwik add` command by adding an `outDir` param
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ jobs:
- run: pnpm install --frozen-lockfile

- name: CLI E2E Tests
run: pnpm run test.e2e-cli
run: pnpm run test.e2e.cli

########### LINT PACKAGES ############
lint-package:
Expand Down
6 changes: 3 additions & 3 deletions e2e/qwik-cli-e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This package provides isolated E2E tests by generating a new application with lo

## Description

Tests can be invoked by running `pnpm run test.e2e-cli`.
Tests can be invoked by running `pnpm run test.e2e.cli`.

**Note that running E2E tests requires the workspace projects to be prebuilt manually!**

Expand All @@ -16,8 +16,8 @@ E2E project does the following internally:

- By default `outputDir` is an auto-generated one using `tmp` npm package. The application that is created here will be removed after the test is executed
- It is possible to install into custom folder using environment variable `TEMP_E2E_PATH`. Here's how the command would look like in this case:
- with absolute path `TEMP_E2E_PATH=/Users/name/projects/tests pnpm run test.e2e-cli`
- with path relative to the qwik workspace `TEMP_E2E_PATH=temp/e2e-folder pnpm run test.e2e-cli`
- with absolute path `TEMP_E2E_PATH=/Users/name/projects/tests pnpm run test.e2e.cli`
- with path relative to the qwik workspace `TEMP_E2E_PATH=temp/e2e-folder pnpm run test.e2e.cli`

Note that provided folder should exist. If custom path is used, generated application will not be removed after the test completes, which is helpful for debugging.

Expand Down
2 changes: 1 addition & 1 deletion e2e/qwik-cli-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"private": true,
"scripts": {
"e2e": "vitest run --config=vite.config.ts",
"e2e:watch": "vitest watch --config=vite.config.ts"
"e2e.watch": "vitest watch --config=vite.config.ts"
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"execa": "8.0.1",
"express": "4.20.0",
"install": "0.13.0",
"memfs": "4.14.0",
"monaco-editor": "0.45.0",
"mri": "1.2.0",
"path-browserify": "1.0.1",
Expand Down Expand Up @@ -241,7 +242,7 @@
"start": "concurrently \"npm:build.watch\" \"npm:tsc.watch\" -n build,tsc -c green,cyan",
"test": "pnpm build.full && pnpm test.unit && pnpm test.e2e",
"test.e2e": "pnpm test.e2e.chromium && pnpm test.e2e.webkit",
"test.e2e-cli": "pnpm --filter qwik-cli-e2e e2e",
"test.e2e.cli": "pnpm --filter qwik-cli-e2e e2e",
"test.e2e.chromium": "playwright test starters --browser=chromium --config starters/playwright.config.ts",
"test.e2e.chromium.debug": "PWDEBUG=1 playwright test starters --browser=chromium --config starters/playwright.config.ts",
"test.e2e.city": "playwright test starters/e2e/qwikcity --browser=chromium --config starters/playwright.config.ts",
Expand Down
16 changes: 10 additions & 6 deletions packages/qwik/src/cli/add/run-add-interactive.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { intro, isCancel, log, outro, select, spinner } from '@clack/prompts';
import { bgBlue, bgMagenta, blue, bold, cyan, magenta } from 'kleur/colors';
import type { IntegrationData, UpdateAppResult } from '../types';
import type { IntegrationData, UpdateAppOptions, UpdateAppResult } from '../types';
import { loadIntegrations, sortIntegrationsAndReturnAsClackOptions } from '../utils/integrations';
import { bye, getPackageManager, note, panic, printHeader } from '../utils/utils';
import { bye, getPackageManager, note, panic } from '../utils/utils';

/* eslint-disable no-console */
import { relative } from 'node:path';
Expand All @@ -16,8 +16,6 @@ export async function runAddInteractive(app: AppCommand, id: string | undefined)
const integrations = await loadIntegrations();
let integration: IntegrationData | undefined;

printHeader();

if (typeof id === 'string') {
// cli passed a flag with the integration id to add
integration = integrations.find((i) => i.id === id);
Expand Down Expand Up @@ -62,11 +60,17 @@ export async function runAddInteractive(app: AppCommand, id: string | undefined)
runInstall = true;
}

const result = await updateApp(pkgManager, {
const updateAppOptions: UpdateAppOptions = {
rootDir: app.rootDir,
integration: integration.id,
installDeps: runInstall,
});
};
const outDir = app.getArg('outDir');
if (outDir) {
updateAppOptions.outDir = outDir;
}

const result = await updateApp(pkgManager, updateAppOptions);

if (app.getArg('skipConfirmation') !== 'true') {
await logUpdateAppResult(pkgManager, result);
Expand Down
15 changes: 10 additions & 5 deletions packages/qwik/src/cli/add/update-files.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import type { FsUpdates, UpdateAppOptions } from '../types';
import { extname, join } from 'node:path';
import type { FsUpdates, UpdateAppOptions } from '../types';
import { getPackageManager } from '../utils/utils';

export async function mergeIntegrationDir(
Expand All @@ -15,24 +15,29 @@ export async function mergeIntegrationDir(
const destName = itemName === 'gitignore' ? '.gitignore' : itemName;
const ext = extname(destName);
const srcChildPath = join(srcDir, itemName);
const destChildPath = join(destDir, destName);
const subOutDir = opts.outDir ? opts.outDir : '';
const destRootPath = join(destDir, destName);
const destChildPath = destRootPath.includes(subOutDir)
? destRootPath
: join(destDir, subOutDir, destName);

const s = await fs.promises.stat(srcChildPath);

if (s.isDirectory()) {
await mergeIntegrationDir(fileUpdates, opts, srcChildPath, destChildPath);
} else if (s.isFile()) {
if (destName === 'package.json') {
await mergePackageJsons(fileUpdates, srcChildPath, destChildPath);
await mergePackageJsons(fileUpdates, srcChildPath, destRootPath);
} else if (destName === 'settings.json') {
await mergeJsons(fileUpdates, srcChildPath, destChildPath);
await mergeJsons(fileUpdates, srcChildPath, destRootPath);
} else if (destName === 'README.md') {
await mergeReadmes(fileUpdates, srcChildPath, destChildPath);
} else if (
destName === '.gitignore' ||
destName === '.prettierignore' ||
destName === '.eslintignore'
) {
await mergeIgnoresFile(fileUpdates, srcChildPath, destChildPath);
await mergeIgnoresFile(fileUpdates, srcChildPath, destRootPath);
} else if (ext === '.css') {
await mergeCss(fileUpdates, srcChildPath, destChildPath, opts);
} else if (fs.existsSync(destChildPath)) {
Expand Down
84 changes: 84 additions & 0 deletions packages/qwik/src/cli/add/update-files.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { fs } from 'memfs';
import { join } from 'path';
import { describe, expect, test, vi } from 'vitest';
import type { FsUpdates, UpdateAppOptions } from '../types';
import { mergeIntegrationDir } from './update-files';

vi.mock('node:fs', () => ({
default: fs,
}));

function setup() {
const fakeSrcDir = 'srcDir';
createFakeFiles(fakeSrcDir);

const fakeDestDir = 'destDir';

const fakeFileUpdates: FsUpdates = {
files: [],
installedDeps: {},
installedScripts: [],
};

const fakeOpts: UpdateAppOptions = {
rootDir: fakeDestDir,
integration: 'integration',
};

return {
fakeSrcDir,
fakeDestDir,
fakeFileUpdates,
fakeOpts,
};
}

describe('mergeIntegrationDir', () => {
test('should merge integration directory', async () => {
const { fakeSrcDir, fakeDestDir, fakeFileUpdates, fakeOpts } = setup();

await mergeIntegrationDir(fakeFileUpdates, fakeOpts, fakeSrcDir, fakeDestDir);

const actualResults = fakeFileUpdates.files.map((f) => f.path);
const expectedResult = ['destDir/fake.ts', 'destDir/package.json', 'destDir/src/global.css'];

expect(actualResults).toEqual(expectedResult);
});

test('should merge integration directory in a monorepo', async () => {
const { fakeSrcDir, fakeDestDir, fakeFileUpdates, fakeOpts } = setup();

// Create a global file in the destination director
const monorepoSubDir = join(fakeDestDir, 'apps', 'subpackage', 'src');
fs.mkdirSync(monorepoSubDir, { recursive: true });
fs.writeFileSync(join(monorepoSubDir, 'global.css'), '/* CSS */');

fakeOpts.outDir = 'apps/subpackage';
fakeOpts.installDeps = true;

await mergeIntegrationDir(fakeFileUpdates, fakeOpts, fakeSrcDir, fakeDestDir);

const actualResults = fakeFileUpdates.files.map((f) => f.path);
const expectedResult = [
`destDir/apps/subpackage/fake.ts`,
`destDir/package.json`,
`destDir/apps/subpackage/src/global.css`,
];

expect(actualResults).toEqual(expectedResult);

const actualGlobalCssContent = fakeFileUpdates.files.find(
(f) => f.path === expectedResult[2]
)?.content;

expect(actualGlobalCssContent).toBe('p{color: red}\n\n/* CSS */\n');
});
});

function createFakeFiles(dir: string) {
// Create fake src files
fs.mkdirSync(join(dir, 'src'), { recursive: true });
fs.writeFileSync(join(dir, 'fake.ts'), 'fake file');
fs.writeFileSync(join(dir, 'package.json'), '{"name": "fake"}');
fs.writeFileSync(join(dir, 'src', 'global.css'), 'p{color: red}');
}
1 change: 1 addition & 0 deletions packages/qwik/src/cli/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface UpdateAppOptions {
rootDir: string;
integration: string;
installDeps?: boolean;
outDir?: string;
}

export interface UpdateAppResult {
Expand Down
Loading

0 comments on commit f4de901

Please sign in to comment.