Skip to content

Commit 65e99b7

Browse files
feat(qwik-nx): qwikNxVite plugin (#66)
1 parent 8e910b1 commit 65e99b7

File tree

22 files changed

+340
-60
lines changed

22 files changed

+340
-60
lines changed

e2e/qwik-nx-e2e/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"executor": "@nrwl/nx-plugin:e2e",
99
"options": {
1010
"target": "qwik-nx:build",
11-
"jestConfig": "e2e/qwik-nx-e2e/jest.config.ts"
11+
"jestConfig": "e2e/qwik-nx-e2e/jest.config.ts",
12+
"maxWorkers": 1
1213
}
1314
}
1415
},

e2e/qwik-nx-e2e/tests/qwik-nx.spec.ts renamed to e2e/qwik-nx-e2e/tests/application.spec.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
killPort,
1212
} from '@qwikifiers/e2e/utils';
1313

14-
describe('qwik-nx e2e', () => {
14+
describe('appGenerator e2e', () => {
1515
// Setting up individual workspaces per
1616
// test can cause e2e runs to take a long time.
1717
// For this reason, we recommend each suite only
@@ -22,10 +22,10 @@ describe('qwik-nx e2e', () => {
2222
ensureNxProject('qwik-nx', 'dist/packages/qwik-nx');
2323
});
2424

25-
afterAll(() => {
25+
afterAll(async () => {
2626
// `nx reset` kills the daemon, and performs
2727
// some work which can help clean up e2e leftovers
28-
runNxCommandAsync('reset');
28+
await runNxCommandAsync('reset');
2929
});
3030

3131
describe('Basic behavior', () => {
@@ -36,6 +36,7 @@ describe('qwik-nx e2e', () => {
3636
`generate qwik-nx:app ${project} --no-interactive`
3737
);
3838
}, 200000);
39+
3940
it('should create qwik-nx', async () => {
4041
const result = await runNxCommandAsync(`build-ssr ${project}`);
4142
expect(result.stdout).toContain(
@@ -64,5 +65,21 @@ describe('qwik-nx e2e', () => {
6465
// ignore
6566
}
6667
}, 200000);
68+
69+
it('should serve application in preview mode with custom port', async () => {
70+
const port = 4232;
71+
const p = await runCommandUntil(
72+
`run ${project}:preview --port=${port}`,
73+
(output) => {
74+
return output.includes('Local:') && output.includes(`:${port}`);
75+
}
76+
);
77+
try {
78+
await promisifiedTreeKill(p.pid, 'SIGKILL');
79+
await killPort(port);
80+
} catch {
81+
// ignore
82+
}
83+
}, 200000);
6784
});
6885
});
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {
2+
checkFilesExist,
3+
ensureNxProject,
4+
readFile,
5+
renameFile,
6+
runNxCommandAsync,
7+
uniq,
8+
updateFile,
9+
} from '@nrwl/nx-plugin/testing';
10+
11+
import {
12+
runCommandUntil,
13+
promisifiedTreeKill,
14+
killPort,
15+
removeFile,
16+
} from '@qwikifiers/e2e/utils';
17+
18+
describe('qwikNxVite plugin e2e', () => {
19+
// Setting up individual workspaces per
20+
// test can cause e2e runs to take a long time.
21+
// For this reason, we recommend each suite only
22+
// consumes 1 workspace. The tests should each operate
23+
// on a unique project in the workspace, such that they
24+
// are not dependant on one another.
25+
beforeAll(() => {
26+
ensureNxProject('qwik-nx', 'dist/packages/qwik-nx');
27+
});
28+
29+
afterAll(async () => {
30+
// `nx reset` kills the daemon, and performs
31+
// some work which can help clean up e2e leftovers
32+
await runNxCommandAsync('reset');
33+
});
34+
35+
describe('should be able to import components from libraries', () => {
36+
let project: string;
37+
let headerLibName: string;
38+
let iconLibName: string;
39+
beforeAll(async () => {
40+
project = uniq('qwik-nx');
41+
headerLibName = uniq('qwik-nx-header');
42+
iconLibName = uniq('qwik-nx-icon');
43+
await runNxCommandAsync(
44+
`generate qwik-nx:app ${project} --no-interactive`
45+
);
46+
await runNxCommandAsync(
47+
`generate qwik-nx:library ${headerLibName} --unitTestRunner=none --no-interactive`
48+
);
49+
await runNxCommandAsync(
50+
`generate qwik-nx:library ${iconLibName} --unitTestRunner=none --no-interactive`
51+
);
52+
53+
// move header component into the library
54+
55+
// update import in layout.tsx
56+
const layoutFilePath = `apps/${project}/src/routes/layout.tsx`;
57+
let layoutFile = readFile(layoutFilePath);
58+
layoutFile = layoutFile.replace(
59+
`import Header from '../components/header/header';`,
60+
`import { Header } from '@proj/${headerLibName}';`
61+
);
62+
updateFile(layoutFilePath, layoutFile);
63+
64+
// move header component files
65+
const headerFolderOldPath = `apps/${project}/src/components/header`;
66+
const headerFolderNewPath = `libs/${headerLibName}/src/lib`;
67+
removeFile(`${headerFolderNewPath}/${headerLibName}.tsx`);
68+
removeFile(`${headerFolderNewPath}/${headerLibName}.css`);
69+
renameFile(
70+
`${headerFolderOldPath}/header.tsx`,
71+
`${headerFolderNewPath}/header.tsx`
72+
);
73+
renameFile(
74+
`${headerFolderOldPath}/header.css`,
75+
`${headerFolderNewPath}/header.css`
76+
);
77+
updateFile(
78+
`libs/${headerLibName}/src/index.ts`,
79+
`export * from './lib/header';`
80+
);
81+
82+
// update header.tsx contents
83+
let headerTsx = readFile(`${headerFolderNewPath}/header.tsx`);
84+
headerTsx = headerTsx.replace(
85+
`import { QwikLogo } from '../icons/qwik';`,
86+
`import { QwikLogo } from '@proj/${iconLibName}';`
87+
);
88+
headerTsx = headerTsx.replace(
89+
'export default component$(() => {',
90+
'export const Header = component$(() => {'
91+
);
92+
updateFile(`${headerFolderNewPath}/header.tsx`, headerTsx);
93+
94+
// move icon component file
95+
const qwikIconFolderNewPath = `libs/${iconLibName}/src/lib`;
96+
removeFile(`${qwikIconFolderNewPath}/${iconLibName}.tsx`);
97+
removeFile(`${qwikIconFolderNewPath}/${iconLibName}.css`);
98+
renameFile(
99+
`apps/${project}/src/components/icons/qwik.tsx`,
100+
`${qwikIconFolderNewPath}/qwik.tsx`
101+
);
102+
updateFile(
103+
`libs/${iconLibName}/src/index.ts`,
104+
`export * from './lib/qwik';`
105+
);
106+
}, 200000);
107+
108+
it('should be able to successfully build the application', async () => {
109+
const result = await runNxCommandAsync(`build-ssr ${project}`);
110+
expect(result.stdout).toContain(
111+
`Successfully ran target build-ssr for project ${project}`
112+
);
113+
expect(() =>
114+
checkFilesExist(`dist/apps/${project}/client/q-manifest.json`)
115+
).not.toThrow();
116+
expect(() =>
117+
checkFilesExist(`dist/apps/${project}/server/entry.preview.mjs`)
118+
).not.toThrow();
119+
}, 200000);
120+
121+
it('should serve application in preview mode with custom port', async () => {
122+
const port = 4212;
123+
const p = await runCommandUntil(
124+
`run ${project}:preview --port=${port}`,
125+
(output) => {
126+
return output.includes('Local:') && output.includes(`:${port}`);
127+
}
128+
);
129+
try {
130+
await promisifiedTreeKill(p.pid, 'SIGKILL');
131+
await killPort(port);
132+
} catch {
133+
// ignore
134+
}
135+
}, 200000);
136+
});
137+
});

e2e/utils/index.ts

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ export const promisifiedTreeKill: (
3434
) => Promise<void> = promisify(treeKill);
3535

3636
export function getNxVersion(): string {
37-
const version = readJsonFile(
37+
const { dependencies } = readJsonFile(
3838
join(workspaceRoot, `./dist/packages/qwik-nx/package.json`)
39-
).peerDependencies.nx;
39+
);
40+
const version = dependencies['@nrwl/vite'];
4041
if (!version) {
4142
throw new Error('Could not retrieve Nx version');
4243
}
@@ -217,44 +218,6 @@ export function expectTestsPass(v: { stdout: string; stderr: string }) {
217218
expect(v.stderr).not.toContain('fail');
218219
}
219220

220-
export function createFile(f: string, content: string = ''): void {
221-
const path = tmpProjPath(f);
222-
createFileSync(path);
223-
if (content) {
224-
updateFile(f, content);
225-
}
226-
}
227-
228-
export function updateFile(
229-
f: string,
230-
content: string | ((content: string) => string)
231-
): void {
232-
ensureDirSync(path.dirname(tmpProjPath(f)));
233-
if (typeof content === 'string') {
234-
writeFileSync(tmpProjPath(f), content);
235-
} else {
236-
writeFileSync(
237-
tmpProjPath(f),
238-
content(readFileSync(tmpProjPath(f)).toString())
239-
);
240-
}
241-
}
242-
243-
export function renameFile(f: string, newPath: string): void {
244-
ensureDirSync(path.dirname(tmpProjPath(newPath)));
245-
renameSync(tmpProjPath(f), tmpProjPath(newPath));
246-
}
247-
248-
export function updateJson<T extends object = any, U extends object = T>(
249-
f: string,
250-
updater: (value: T) => U
251-
) {
252-
updateFile(f, (s) => {
253-
const json = JSON.parse(s);
254-
return JSON.stringify(updater(json), null, 2);
255-
});
256-
}
257-
258221
export function checkFilesDoNotExist(...expectedFiles: string[]) {
259222
expectedFiles.forEach((f) => {
260223
const ff = f.startsWith('/') ? f : tmpProjPath(f);

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"private": true,
1414
"devDependencies": {
15+
"@builder.io/qwik": "0.17.4",
1516
"@commitlint/cli": "^17.3.0",
1617
"@commitlint/config-angular": "^17.3.0",
1718
"@commitlint/config-conventional": "^17.3.0",
@@ -55,7 +56,8 @@
5556
"tree-kill": "1.2.2",
5657
"ts-jest": "28.0.5",
5758
"ts-node": "10.9.1",
58-
"typescript": "~4.8.2"
59+
"typescript": "~4.8.2",
60+
"vite": "4.1.1"
5961
},
6062
"dependencies": {
6163
"@swc/helpers": "~0.4.11",

packages/qwik-nx/generators.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src/generators';

packages/qwik-nx/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// This is explicitly empty, but serves as a primary compilation entry-point.
2+
export default {};

packages/qwik-nx/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "qwik-nx",
33
"version": "0.8.0",
4-
"main": "src/index.js",
4+
"main": "./index.js",
5+
"typings": "./index.d.ts",
56
"license": "MIT",
67
"author": "Shai Reznik",
78
"description": "Nx plugin for qwik",
@@ -22,5 +23,9 @@
2223
"executors": "./executors.json",
2324
"dependencies": {
2425
"@nrwl/vite": "~15.6.0"
26+
},
27+
"peerDependencies": {
28+
"@builder.io/qwik": "^0.16.0",
29+
"vite": "~4.1.1"
2530
}
2631
}

packages/qwik-nx/plugins.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src/plugins';

packages/qwik-nx/src/generators/application/files/vite.config.ts__template__

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { qwikVite } from '@builder.io/qwik/optimizer';
22
import { qwikCity } from '@builder.io/qwik-city/vite';
33
import { defineConfig } from 'vite';
44
import tsconfigPaths from 'vite-tsconfig-paths';
5+
import { qwikNxVite } from 'qwik-nx/plugins';
56

67
export default defineConfig({
78
plugins: [
9+
qwikNxVite(),
810
qwikCity(),
911
qwikVite({
1012
client: {
@@ -14,7 +16,7 @@ export default defineConfig({
1416
outDir: '<%= offsetFromRoot %>dist/<%= projectRoot %>/server',
1517
},
1618
}),
17-
tsconfigPaths()
19+
tsconfigPaths({ root: '<%= offsetFromRoot %>' })
1820
],
1921
server: {
2022
fs: {

0 commit comments

Comments
 (0)