Skip to content

Commit 6529be9

Browse files
feat(vite): add tsconfig paths resolution plugin (#17844)
1 parent ce42f04 commit 6529be9

File tree

16 files changed

+422
-133
lines changed

16 files changed

+422
-133
lines changed

e2e/vite/src/vite.test.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { names } from '@nx/devkit';
12
import {
23
cleanupProject,
34
createFile,
@@ -240,6 +241,74 @@ describe('Vite Plugin', () => {
240241
100_000;
241242
});
242243

244+
describe('incremental building', () => {
245+
const app = uniq('demo');
246+
const lib = uniq('my-lib');
247+
beforeAll(() => {
248+
proj = newProject({ name: uniq('vite-incr-build') });
249+
runCLI(`generate @nx/react:app ${app} --bundler=vite --no-interactive`);
250+
251+
// only this project will be directly used from dist
252+
runCLI(
253+
`generate @nx/react:lib ${lib}-buildable --unitTestRunner=none --bundler=vite --importPath="@acme/buildable" --no-interactive`
254+
);
255+
256+
runCLI(
257+
`generate @nx/react:lib ${lib} --unitTestRunner=none --bundler=none --importPath="@acme/non-buildable" --no-interactive`
258+
);
259+
260+
// because the default js lib builds as cjs it cannot be loaded from dist
261+
// so the paths plugin should always resolve to the libs source
262+
runCLI(
263+
`generate @nx/js:lib ${lib}-js --bundler=tsc --importPath="@acme/js-lib" --no-interactive`
264+
);
265+
const buildableLibCmp = names(`${lib}-buildable`).className;
266+
const nonBuildableLibCmp = names(lib).className;
267+
const buildableJsLibFn = names(`${lib}-js`).propertyName;
268+
269+
updateFile(`apps/${app}/src/app/app.tsx`, () => {
270+
return `// eslint-disable-next-line @typescript-eslint/no-unused-vars
271+
import styles from './app.module.css';
272+
273+
import NxWelcome from './nx-welcome';
274+
import { ${buildableLibCmp} } from '@acme/buildable';
275+
import { ${buildableJsLibFn} } from '@acme/js-lib';
276+
import { ${nonBuildableLibCmp} } from '@acme/non-buildable';
277+
278+
export function App() {
279+
return (
280+
<div>
281+
<${buildableLibCmp} />
282+
<${nonBuildableLibCmp} />
283+
<p>{${buildableJsLibFn}()}</p>
284+
<NxWelcome title="${app}" />
285+
</div>
286+
);
287+
}
288+
export default App;
289+
`;
290+
});
291+
});
292+
293+
afterAll(() => {
294+
cleanupProject();
295+
});
296+
297+
it('should build app from libs source', () => {
298+
const results = runCLI(`build ${app} --buildLibsFromSource=true`);
299+
expect(results).toContain('Successfully ran target build for project');
300+
// this should be more modules than build from dist
301+
expect(results).toContain('40 modules transformed');
302+
});
303+
304+
it('should build app from libs dist', () => {
305+
const results = runCLI(`build ${app} --buildLibsFromSource=false`);
306+
expect(results).toContain('Successfully ran target build for project');
307+
// this should be less modules than building from source
308+
expect(results).toContain('38 modules transformed');
309+
});
310+
});
311+
243312
describe('should be able to create libs that use vitest', () => {
244313
const lib = uniq('my-lib');
245314
beforeEach(() => {
@@ -255,7 +324,6 @@ describe('Vite Plugin', () => {
255324
`Successfully ran target test for project ${lib}`
256325
);
257326

258-
// TODO(caleb): run tests from project root and make sure they still work
259327
const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
260328
cwd: `${tmpProjPath()}/libs/${lib}`,
261329
});
@@ -270,7 +338,7 @@ describe('Vite Plugin', () => {
270338
return `/// <reference types="vitest" />
271339
import { defineConfig } from 'vite';
272340
import react from '@vitejs/plugin-react';
273-
import viteTsConfigPaths from 'vite-tsconfig-paths';
341+
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
274342
275343
export default defineConfig({
276344
server: {
@@ -279,9 +347,7 @@ describe('Vite Plugin', () => {
279347
},
280348
plugins: [
281349
react(),
282-
viteTsConfigPaths({
283-
root: './',
284-
}),
350+
nxViteTsPaths()
285351
],
286352
test: {
287353
globals: true,
@@ -321,7 +387,8 @@ describe('Vite Plugin', () => {
321387
updateFile(`libs/${lib}/vite.config.ts`, () => {
322388
return `import { defineConfig } from 'vite';
323389
import react from '@vitejs/plugin-react';
324-
import viteTsConfigPaths from 'vite-tsconfig-paths';
390+
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
391+
325392
326393
export default defineConfig({
327394
server: {
@@ -330,9 +397,7 @@ export default defineConfig({
330397
},
331398
plugins: [
332399
react(),
333-
viteTsConfigPaths({
334-
root: './',
335-
}),
400+
nxViteTsPaths()
336401
],
337402
test: {
338403
globals: true,

packages/vite/migrations.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"description": "Changes the testFile config in the vite:test exectutor from a string to an array of strings",
3030
"cli": "nx",
3131
"implementation": "./src/migrations/update-16-4-1-update-test-file-config/update-16-4-1-test-file-config"
32+
},
33+
"16-6-0-change-ts-paths-plugin": {
34+
"version": "16.6.0-beta.4",
35+
"description": "Change vite-tsconfig-paths plugin for first party nx-vite-tsconfig-paths plugin",
36+
"cli": "nx",
37+
"implementation": "./src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin"
3238
}
3339
},
3440
"packageJsonUpdates": {

packages/vite/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"dotenv": "~10.0.0",
3535
"enquirer": "~2.3.6",
3636
"@nx/devkit": "file:../devkit",
37-
"@nx/js": "file:../js"
37+
"@nx/js": "file:../js",
38+
"tsconfig-paths": "^4.1.2"
3839
},
3940
"peerDependencies": {
4041
"vite": "^4.3.4",
@@ -53,6 +54,7 @@
5354
"./executors": "./executors.js",
5455
"./src/executors/*/schema.json": "./src/executors/*/schema.json",
5556
"./src/executors/*.impl": "./src/executors/*.impl.js",
56-
"./src/executors/*/compat": "./src/executors/*/compat.js"
57+
"./src/executors/*/compat": "./src/executors/*/compat.js",
58+
"./plugins/nx-tsconfig-paths.plugin": "./plugins/nx-tsconfig-paths.plugin.js"
5759
}
5860
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { stripIndents, workspaceRoot } from '@nx/devkit';
2+
import { existsSync } from 'node:fs';
3+
import { relative, join, resolve } from 'node:path';
4+
import { loadConfig, createMatchPath, MatchPath } from 'tsconfig-paths';
5+
6+
export function nxViteTsPaths() {
7+
let matchTsPathEsm: MatchPath;
8+
let matchTsPathFallback: MatchPath | undefined;
9+
10+
return {
11+
name: 'nx-vite-ts-paths',
12+
configResolved(config: any) {
13+
const projectRoot = config.root;
14+
const projectRootFromWorkspaceRoot = relative(workspaceRoot, projectRoot);
15+
16+
const foundTsConfigPath = getTsConfig(
17+
join(
18+
workspaceRoot,
19+
'tmp',
20+
projectRootFromWorkspaceRoot,
21+
'tsconfig.generated.json'
22+
)
23+
);
24+
if (!foundTsConfigPath) {
25+
throw new Error(stripIndents`Unable to find a tsconfig in the workspace!
26+
There should at least be a tsconfig.base.json or tsconfig.json in the root of the workspace ${workspaceRoot}`);
27+
}
28+
const parsed = loadConfig(foundTsConfigPath);
29+
30+
logIt('first parsed tsconfig: ', parsed);
31+
if (parsed.resultType === 'failed') {
32+
throw new Error(`Failed loading tsonfig at ${foundTsConfigPath}`);
33+
}
34+
35+
matchTsPathEsm = createMatchPath(parsed.absoluteBaseUrl, parsed.paths, [
36+
['exports', '.', 'import'],
37+
'module',
38+
'main',
39+
]);
40+
41+
const rootLevelTsConfig = getTsConfig(
42+
join(workspaceRoot, 'tsconfig.base.json')
43+
);
44+
const rootLevelParsed = loadConfig(rootLevelTsConfig);
45+
logIt('fallback parsed tsconfig: ', rootLevelParsed);
46+
if (rootLevelParsed.resultType === 'success') {
47+
matchTsPathFallback = createMatchPath(
48+
rootLevelParsed.absoluteBaseUrl,
49+
rootLevelParsed.paths,
50+
['main', 'module']
51+
);
52+
}
53+
},
54+
resolveId(source: string) {
55+
let resolvedFile: string;
56+
try {
57+
resolvedFile = matchTsPathEsm(source);
58+
} catch (e) {
59+
logIt('Using fallback path matching.');
60+
resolvedFile = matchTsPathFallback?.(source);
61+
}
62+
63+
if (!resolvedFile) {
64+
logIt(`Unable to resolve ${source} with tsconfig paths`);
65+
}
66+
67+
return resolvedFile;
68+
},
69+
};
70+
}
71+
72+
function getTsConfig(preferredTsConfigPath: string): string {
73+
return [
74+
resolve(preferredTsConfigPath),
75+
resolve(join(workspaceRoot, 'tsconfig.base.json')),
76+
resolve(join(workspaceRoot, 'tsconfig.json')),
77+
].find((tsPath) => {
78+
if (existsSync(tsPath)) {
79+
logIt('Found tsconfig at', tsPath);
80+
return tsPath;
81+
}
82+
});
83+
}
84+
85+
function logIt(...msg: any[]) {
86+
if (process.env.NX_VERBOSE_LOGGING === 'true') {
87+
console.debug('[Nx Vite TsPaths]', ...msg);
88+
}
89+
}

packages/vite/src/executors/build/build.impl.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {
2121
import { existsSync, writeFileSync } from 'fs';
2222
import { resolve } from 'path';
2323
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
24-
import { registerPaths, validateTypes } from '../../utils/executor-utils';
24+
import {
25+
createBuildableTsConfig,
26+
validateTypes,
27+
} from '../../utils/executor-utils';
2528

2629
export async function* viteBuildExecutor(
2730
options: ViteBuildExecutorOptions,
@@ -30,7 +33,7 @@ export async function* viteBuildExecutor(
3033
const projectRoot =
3134
context.projectsConfigurations.projects[context.projectName].root;
3235

33-
registerPaths(projectRoot, options, context);
36+
createBuildableTsConfig(projectRoot, options, context);
3437

3538
const normalizedOptions = normalizeOptions(options);
3639

packages/vite/src/executors/dev-server/dev-server.impl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111

1212
import { ViteDevServerExecutorOptions } from './schema';
1313
import { ViteBuildExecutorOptions } from '../build/schema';
14-
import { registerPaths } from '../../utils/executor-utils';
14+
import { createBuildableTsConfig } from '../../utils/executor-utils';
1515

1616
export async function* viteDevServerExecutor(
1717
options: ViteDevServerExecutorOptions,
@@ -20,7 +20,7 @@ export async function* viteDevServerExecutor(
2020
const projectRoot =
2121
context.projectsConfigurations.projects[context.projectName].root;
2222

23-
registerPaths(projectRoot, options, context);
23+
createBuildableTsConfig(projectRoot, options, context);
2424

2525
// Retrieve the option for the configured buildTarget.
2626
const buildTargetOptions: ViteBuildExecutorOptions = getNxTargetOptions(

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy