Skip to content

Commit

Permalink
feat(vite): add tsconfig paths resolution plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
barbados-clemens committed Jun 29, 2023
1 parent 7d15e0c commit 1a271ee
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 11 deletions.
70 changes: 69 additions & 1 deletion e2e/vite/src/vite.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { names } from '@nx/devkit';
import {
cleanupProject,
createFile,
Expand Down Expand Up @@ -240,6 +241,74 @@ describe('Vite Plugin', () => {
100_000;
});

describe.only('incremental building', () => {
const app = uniq('demo');
const lib = uniq('my-lib');
beforeAll(() => {
proj = newProject({ name: uniq('vite-incr-build') });
runCLI(`generate @nx/react:app ${app} --bundler=vite --no-interactive`);

// only this project will be directly used from dist
runCLI(
`generate @nx/react:lib ${lib}-buildable --unitTestRunner=none --bundler=vite --importPath="@acme/buildable" --no-interactive`
);

runCLI(
`generate @nx/react:lib ${lib} --unitTestRunner=none --bundler=none --importPath="@acme/non-buildable" --no-interactive`
);

// because the default js lib builds as cjs it cannot be loaded from dist
// so the paths plugin should always resolve to the libs source
runCLI(
`generate @nx/js:lib ${lib}-js --bundler=tsc --importPath="@acme/js-lib" --no-interactive`
);
const buildableLibCmp = names(`${lib}-buildable`).className;
const nonBuildableLibCmp = names(lib).className;
const buildableJsLibFn = names(`${lib}-js`).propertyName;

updateFile(`apps/${app}/src/app/app.tsx`, () => {
return `// eslint-disable-next-line @typescript-eslint/no-unused-vars
import styles from './app.module.css';
import NxWelcome from './nx-welcome';
import { ${buildableLibCmp} } from '@acme/buildable';
import { ${buildableJsLibFn} } from '@acme/js-lib';
import { ${nonBuildableLibCmp} } from '@acme/non-buildable';
export function App() {
return (
<div>
<${buildableLibCmp} />
<${nonBuildableLibCmp} />
<p>{${buildableJsLibFn}()}</p>
<NxWelcome title="${app}" />
</div>
);
}
export default App;
`;
});
});

afterAll(() => {
cleanupProject();
});

it('should build app from libs source', () => {
const results = runCLI(`build ${app} --buildLibsFromSource=true`);
expect(results).toContain('Successfully ran target build for project');
// this should be more modules than build from dist
expect(results).toContain('40 modules transformed');
});

it('should build app from libs dist', () => {
const results = runCLI(`build ${app} --buildLibsFromSource=false`);
expect(results).toContain('Successfully ran target build for project');
// this should be less modules than building from source
expect(results).toContain('38 modules transformed');
});
});

describe('should be able to create libs that use vitest', () => {
const lib = uniq('my-lib');
beforeEach(() => {
Expand All @@ -255,7 +324,6 @@ describe('Vite Plugin', () => {
`Successfully ran target test for project ${lib}`
);

// TODO(caleb): run tests from project root and make sure they still work
const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
cwd: `${tmpProjPath()}/libs/${lib}`,
});
Expand Down
6 changes: 4 additions & 2 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"dotenv": "~10.0.0",
"enquirer": "~2.3.6",
"@nx/devkit": "file:../devkit",
"@nx/js": "file:../js"
"@nx/js": "file:../js",
"tsconfig-paths": "^4.1.2"
},
"peerDependencies": {
"vite": "^4.3.4",
Expand All @@ -53,6 +54,7 @@
"./executors": "./executors.js",
"./src/executors/*/schema.json": "./src/executors/*/schema.json",
"./src/executors/*.impl": "./src/executors/*.impl.js",
"./src/executors/*/compat": "./src/executors/*/compat.js"
"./src/executors/*/compat": "./src/executors/*/compat.js",
"./plugins/nx-tsconfig-paths.plugin": "./plugins/nx-tsconfig-paths.plugin.js"
}
}
91 changes: 91 additions & 0 deletions packages/vite/plugins/nx-tsconfig-paths.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { stripIndents, workspaceRoot } from '@nx/devkit';
import { existsSync } from 'node:fs';
import { relative, join, resolve, posix } from 'node:path';
import { loadConfig, createMatchPath, MatchPath } from 'tsconfig-paths';

// TODO(caleb): should we provide a way to override anything for the plugin?

export function nxViteTsPaths() {
let matchTsPathEsm: MatchPath;
let matchTsPathFallback: MatchPath | undefined;

return {
name: 'nx-vite-ts-paths',
configResolved(config: any) {
const projectRoot = config.root;
// TODO(caleb): verify on windows to see what type of paths vite returns posix vs win32
const projectRootFromWorkspaceRoot = relative(workspaceRoot, projectRoot);

const foundTsConfigPath = getTsConfig(
join(
workspaceRoot,
'tmp',
projectRootFromWorkspaceRoot,
'tsconfig.generated.json'
)
);
if (!foundTsConfigPath) {
throw new Error(stripIndents`Unable to find a tsconfig in the workspace!
There should at least be a tsconfig.base.json or tsconfig.json in the root of the workspace ${workspaceRoot}`);
}
const parsed = loadConfig(foundTsConfigPath);

logIt('first parsed tsconfig: ', parsed);
if (parsed.resultType === 'failed') {
throw new Error(`Failed loading tsonfig at ${foundTsConfigPath}`);
}

matchTsPathEsm = createMatchPath(parsed.absoluteBaseUrl, parsed.paths, [
['exports', '.', 'import'],
'main',
]);

const rootLevelTsConfig = getTsConfig(
join(workspaceRoot, 'tsconfig.base.json')
);
const rootLevelParsed = loadConfig(rootLevelTsConfig);
logIt('fallback parsed tsconfig: ', rootLevelParsed);
if (rootLevelParsed.resultType === 'success') {
matchTsPathFallback = createMatchPath(
rootLevelParsed.absoluteBaseUrl,
rootLevelParsed.paths,
['main', 'module']
);
}
},
resolveId(source: string) {
let resolvedFile: string;
try {
resolvedFile = matchTsPathEsm(source);
} catch (e) {
logIt('Using fallback path matching.');
resolvedFile = matchTsPathFallback?.(source);
}

if (!resolvedFile) {
logIt(`Unable to resolve ${source} with tsconfig paths`);
}

return resolvedFile;
},
};
}

function getTsConfig(preferredTsConfigPath: string): string {
return [
resolve(preferredTsConfigPath),
resolve(join(workspaceRoot, 'tsconfig.base.json')),
resolve(join(workspaceRoot, 'tsconfig.json')),
].find((tsPath) => {
if (existsSync(tsPath)) {
logIt('Found tsconfig at', tsPath);
return tsPath;
}
});
}

function logIt(...msg: any[]) {
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.debug('[Nx Vite TsPaths]', ...msg);
}
}
1 change: 1 addition & 0 deletions packages/vite/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export async function* viteBuildExecutor(
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;

// TODO(caleb): do we need to register the paths anymore or just make tsconfig?
registerPaths(projectRoot, options, context);

const normalizedOptions = normalizeOptions(options);
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/executors/dev-server/dev-server.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export async function* viteDevServerExecutor(
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;

// TODO(caleb): do we need to register the paths anymore or just make tsconfig?
registerPaths(projectRoot, options, context);

// Retrieve the option for the configured buildTarget.
Expand Down
7 changes: 3 additions & 4 deletions packages/vite/src/utils/executor-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export function registerPaths(
const tsConfig = resolve(projectRoot, 'tsconfig.json');
options.buildLibsFromSource ??= true;

if (!options.buildLibsFromSource) {
if (options.buildLibsFromSource) {
registerTsConfigPaths(tsConfig);
} else {
const { dependencies } = calculateProjectDependencies(
context.projectGraph,
context.root,
Expand All @@ -49,9 +51,6 @@ export function registerPaths(
projectRoot,
dependencies
);

registerTsConfigPaths(tmpTsConfig);
} else {
registerTsConfigPaths(tsConfig);
}
}
9 changes: 5 additions & 4 deletions packages/vite/src/utils/generator-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,13 +583,14 @@ export function createOrEditViteConfig(
host: 'localhost',
},`;

// viteTsConfigPaths({
// root: '${offsetFromRoot(projectConfig.root)}',
// }),
const pluginOption = `
plugins: [
${dtsPlugin}
${reactPlugin}
viteTsConfigPaths({
root: '${offsetFromRoot(projectConfig.root)}',
}),
nxViteTsPaths(),
],
`;

Expand Down Expand Up @@ -628,7 +629,7 @@ export function createOrEditViteConfig(
/// <reference types="vitest" />
import { defineConfig } from 'vite';
${reactPluginImportLine}
import viteTsConfigPaths from 'vite-tsconfig-paths';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
${dtsImportLine}
export default defineConfig({
Expand Down

0 comments on commit 1a271ee

Please sign in to comment.