Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(vite): resolves files with dot suffixes correctly #28518

Merged
merged 4 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/vite/src/vite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe('@nx/vite/plugin', () => {
`generate @nx/react:library libs/${mylib} --bundler=none --unitTestRunner=vitest`
);
updateFile(`libs/${mylib}/src/styles.css`, `.foo {}`);
updateFile(`libs/${mylib}/src/foo.mts`, `export const foo = 'foo';`);
updateFile(`libs/${mylib}/src/foo.mjs`, `export const foo = 'foo';`);
updateFile(
`libs/${mylib}/src/foo.spec.ts`,
`
Expand Down
28 changes: 4 additions & 24 deletions packages/vite/plugins/nx-tsconfig-paths.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
workspaceRoot,
} from '@nx/devkit';
import { copyFileSync, existsSync } from 'node:fs';
import { join, parse, relative, resolve } from 'node:path';
import { join, relative, resolve } from 'node:path';
import {
loadConfig,
createMatchPath,
Expand All @@ -18,6 +18,7 @@ import {
} from '@nx/js/src/utils/buildable-libs-utils';
import { Plugin } from 'vite';
import { nxViteBuildCoordinationPlugin } from './nx-vite-build-coordination.plugin';
import { findFile } from '../src/utils/nx-tsconfig-paths-find-file';
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';

export interface nxViteTsPathsOptions {
Expand Down Expand Up @@ -243,33 +244,12 @@ There should at least be a tsconfig.base.json or tsconfig.json in the root of th
);

resolvedFile = findFile(
importPath.replace(normalizedImport, joinedPath)
importPath.replace(normalizedImport, joinedPath),
options.extensions
);
}
}

return resolvedFile;
}

function findFile(path: string): string {
for (const ext of options.extensions) {
// Support file with "." in the name.
let resolvedPath = resolve(path + ext);
if (existsSync(resolvedPath)) {
return resolvedPath;
}

// Support file extensions such as .css and .js in the import path.
const { dir, name } = parse(path);
resolvedPath = resolve(dir, name + ext);
if (existsSync(resolvedPath)) {
return resolvedPath;
}

const resolvedIndexPath = resolve(path, `index${ext}`);
if (existsSync(resolvedIndexPath)) {
return resolvedIndexPath;
}
}
}
}
123 changes: 123 additions & 0 deletions packages/vite/src/utils/nx-tsconfig-paths-find-file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { findFile as findFileMain } from './nx-tsconfig-paths-find-file';

describe('@nx/vite nx-tsconfig-paths-find-file', () => {
const extensions = ['.ts', '.js', '.mts'];
const fs = new Set<string>();
const existsSyncImpl = (path: string) => fs.has(path);
const findFile = (path: string, exts: string[] = extensions): string =>
findFileMain(path, exts, existsSyncImpl);

beforeAll(() => {
[
'/dir1/file.ts',
'/dir1/file.suffix.ts',
'/dir2/inner/index.ts',
'/dir2/inner/index.js',
'/dir3/file.js',
'/dir4/file.css',
'/dir5/file.suffix.ts.js',
'/dir6/inner.suffix/index.ts',
'/file1.mts',
].forEach((item) => fs.add(item));
});

afterAll(() => {
fs.clear();
});

const cases: Array<{
title: string;
path: string;
expected: string | undefined;
extensions?: string[];
}> = [
{
title: 'Should return undefined for missing file',
path: '/dir10/file',
expected: undefined,
},
{
title: 'Should return undefined for missing index file',
path: '/dir10/inner',
expected: undefined,
},
{
title: 'Should return existing file path with extension',
path: '/dir1/file',
expected: '/dir1/file.ts',
},
{
title:
'Should return correct file in case with same filename but one with suffix',
path: '/dir1/file.suffix',
expected: '/dir1/file.suffix.ts',
},
{
title: 'Should return existing file with dir request',
path: '/dir2/inner',
expected: '/dir2/inner/index.ts',
},
{
title: 'Should return existing file with index request',
path: '/dir2/inner/index',
expected: '/dir2/inner/index.ts',
},
{
title: 'Should return existing file with js extension',
path: '/dir3/file',
expected: '/dir3/file.js',
},
{
title: 'Should return undefined for non presented extension',
path: '/dir4/file',
expected: undefined,
},
{
title: 'Should return undefined for unknown file',
path: '/dir5/file.suffix',
expected: undefined,
},
{
title: 'Should return js file with strange suffix filename',
path: '/dir5/file.suffix.ts',
expected: '/dir5/file.suffix.ts.js',
},
{
title: 'Should return index file for dir with suffixed name',
path: '/dir6/inner.suffix',
expected: '/dir6/inner.suffix/index.ts',
},
{
title: 'Should return file for import with extension',
path: '/dir1/file.ts',
expected: '/dir1/file.ts',
},
{
title: 'Should return file with .js ext instead of .ts',
path: '/dir2/inner/index.js',
expected: '/dir2/inner/index.js',
},
{
title: 'Should return css file that imported with query',
path: '/dir4/file.css?inline',
expected: '/dir4/file.css',
extensions: ['.js', '.css'],
},
{
title: 'Should return file with .mts',
path: '/file1.mts',
expected: '/file1.mts',
},
{
title: 'Should return file',
path: '/file1',
expected: '/file1.mts',
},
];

cases.forEach(({ title, path, expected, extensions }) => {
it(title, () => {
expect(findFile(path, extensions)).toEqual(expected);
});
});
});
27 changes: 27 additions & 0 deletions packages/vite/src/utils/nx-tsconfig-paths-find-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { existsSync } from 'node:fs';
import { resolve, basename, dirname } from 'node:path';

export function findFile(
path: string,
extensions: string[],
existsSyncImpl: typeof existsSync = existsSync
): string {
const queryLessPath = path.replace(/\?\S*$/, '');

for (const ext of extensions) {
const dir = dirname(path);
// Support file extensions such as .css and .js in the import path.
// While still allowing for '.suffix'
const name = basename(queryLessPath, ext);

const resolvedPath = resolve(dir, name + ext);
if (existsSyncImpl(resolvedPath)) {
return resolvedPath;
}

const resolvedIndexPath = resolve(path, `index${ext}`);
if (existsSyncImpl(resolvedIndexPath)) {
return resolvedIndexPath;
}
}
}
Loading