fix(bundling): fallback to manual file resolution if tsconfig-paths fails (#18477)
This commit is contained in:
parent
cf1175f2ae
commit
e3b513b6c0
@ -11,6 +11,7 @@ import {
|
||||
promisifiedTreeKill,
|
||||
readFile,
|
||||
readJson,
|
||||
removeFile,
|
||||
rmDist,
|
||||
runCLI,
|
||||
runCLIAsync,
|
||||
@ -267,9 +268,8 @@ describe('Vite Plugin', () => {
|
||||
const buildableJsLibFn = names(`${lib}-js`).propertyName;
|
||||
|
||||
updateFile(`apps/${app}/src/app/app.tsx`, () => {
|
||||
return `// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return `
|
||||
import styles from './app.module.css';
|
||||
|
||||
import NxWelcome from './nx-welcome';
|
||||
import { ${buildableLibCmp} } from '@acme/buildable';
|
||||
import { ${buildableJsLibFn} } from '@acme/js-lib';
|
||||
@ -277,12 +277,12 @@ import { ${nonBuildableLibCmp} } from '@acme/non-buildable';
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<div>
|
||||
<${buildableLibCmp} />
|
||||
<${nonBuildableLibCmp} />
|
||||
<p>{${buildableJsLibFn}()}</p>
|
||||
<NxWelcome title="${app}" />
|
||||
</div>
|
||||
<div>
|
||||
<${buildableLibCmp} />
|
||||
<${nonBuildableLibCmp} />
|
||||
<p>{${buildableJsLibFn}()}</p>
|
||||
<NxWelcome title="${app}" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default App;
|
||||
@ -307,6 +307,24 @@ export default App;
|
||||
// this should be less modules than building from source
|
||||
expect(results).toContain('38 modules transformed');
|
||||
});
|
||||
|
||||
it('should build app from libs without package.json in lib', () => {
|
||||
removeFile(`libs/${lib}-buildable/package.json`);
|
||||
|
||||
const buildFromSourceResults = runCLI(
|
||||
`build ${app} --buildLibsFromSource=true`
|
||||
);
|
||||
expect(buildFromSourceResults).toContain(
|
||||
'Successfully ran target build for project'
|
||||
);
|
||||
|
||||
const noBuildFromSourceResults = runCLI(
|
||||
`build ${app} --buildLibsFromSource=false`
|
||||
);
|
||||
expect(noBuildFromSourceResults).toContain(
|
||||
'Successfully ran target build for project'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should be able to create libs that use vitest', () => {
|
||||
|
||||
@ -1,11 +1,49 @@
|
||||
import { stripIndents, workspaceRoot } from '@nx/devkit';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { relative, join, resolve } from 'node:path';
|
||||
import { loadConfig, createMatchPath, MatchPath } from 'tsconfig-paths';
|
||||
import {
|
||||
loadConfig,
|
||||
createMatchPath,
|
||||
MatchPath,
|
||||
ConfigLoaderSuccessResult,
|
||||
} from 'tsconfig-paths';
|
||||
|
||||
export function nxViteTsPaths() {
|
||||
export interface nxViteTsPathsOptions {
|
||||
/**
|
||||
* Enable debug logging
|
||||
* @default false
|
||||
**/
|
||||
debug?: boolean;
|
||||
/**
|
||||
* export fields in package.json to use for resolving
|
||||
* @default [['exports', '.', 'import'], 'module', 'main']
|
||||
*
|
||||
* fallback resolution will use ['main', 'module']
|
||||
**/
|
||||
mainFields?: (string | string[])[];
|
||||
/**
|
||||
* extensions to check when resolving files when package.json resolution fails
|
||||
* @default ['.ts', '.tsx', '.js', '.jsx', '.json', '.mjs', '.cjs']
|
||||
**/
|
||||
extensions?: string[];
|
||||
}
|
||||
|
||||
export function nxViteTsPaths(options: nxViteTsPathsOptions = {}) {
|
||||
let matchTsPathEsm: MatchPath;
|
||||
let matchTsPathFallback: MatchPath | undefined;
|
||||
let tsConfigPathsEsm: ConfigLoaderSuccessResult;
|
||||
let tsConfigPathsFallback: ConfigLoaderSuccessResult;
|
||||
|
||||
options.extensions ??= [
|
||||
'.ts',
|
||||
'.tsx',
|
||||
'.js',
|
||||
'.jsx',
|
||||
'.json',
|
||||
'.mjs',
|
||||
'.cjs',
|
||||
];
|
||||
options.mainFields ??= [['exports', '.', 'import'], 'module', 'main'];
|
||||
|
||||
return {
|
||||
name: 'nx-vite-ts-paths',
|
||||
@ -31,12 +69,13 @@ There should at least be a tsconfig.base.json or tsconfig.json in the root of th
|
||||
if (parsed.resultType === 'failed') {
|
||||
throw new Error(`Failed loading tsonfig at ${foundTsConfigPath}`);
|
||||
}
|
||||
tsConfigPathsEsm = parsed;
|
||||
|
||||
matchTsPathEsm = createMatchPath(parsed.absoluteBaseUrl, parsed.paths, [
|
||||
['exports', '.', 'import'],
|
||||
'module',
|
||||
'main',
|
||||
]);
|
||||
matchTsPathEsm = createMatchPath(
|
||||
parsed.absoluteBaseUrl,
|
||||
parsed.paths,
|
||||
options.mainFields
|
||||
);
|
||||
|
||||
const rootLevelTsConfig = getTsConfig(
|
||||
join(workspaceRoot, 'tsconfig.base.json')
|
||||
@ -44,6 +83,7 @@ There should at least be a tsconfig.base.json or tsconfig.json in the root of th
|
||||
const rootLevelParsed = loadConfig(rootLevelTsConfig);
|
||||
logIt('fallback parsed tsconfig: ', rootLevelParsed);
|
||||
if (rootLevelParsed.resultType === 'success') {
|
||||
tsConfigPathsFallback = rootLevelParsed;
|
||||
matchTsPathFallback = createMatchPath(
|
||||
rootLevelParsed.absoluteBaseUrl,
|
||||
rootLevelParsed.paths,
|
||||
@ -51,39 +91,82 @@ There should at least be a tsconfig.base.json or tsconfig.json in the root of th
|
||||
);
|
||||
}
|
||||
},
|
||||
resolveId(source: string) {
|
||||
resolveId(importPath: string) {
|
||||
let resolvedFile: string;
|
||||
try {
|
||||
resolvedFile = matchTsPathEsm(source);
|
||||
resolvedFile = matchTsPathEsm(importPath);
|
||||
} catch (e) {
|
||||
logIt('Using fallback path matching.');
|
||||
resolvedFile = matchTsPathFallback?.(source);
|
||||
resolvedFile = matchTsPathFallback?.(importPath);
|
||||
}
|
||||
|
||||
if (!resolvedFile) {
|
||||
logIt(`Unable to resolve ${source} with tsconfig paths`);
|
||||
if (tsConfigPathsEsm || tsConfigPathsFallback) {
|
||||
logIt(
|
||||
`Unable to resolve ${importPath} with tsconfig paths. Using fallback file matching.`
|
||||
);
|
||||
resolvedFile =
|
||||
loadFileFromPaths(tsConfigPathsEsm, importPath) ||
|
||||
loadFileFromPaths(tsConfigPathsFallback, importPath);
|
||||
} else {
|
||||
logIt(`Unable to resolve ${importPath} with tsconfig paths`);
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedFile;
|
||||
logIt(`Resolved ${importPath} to ${resolvedFile}`);
|
||||
// Returning null defers to other resolveId functions and eventually the default resolution behavior
|
||||
// https://rollupjs.org/plugin-development/#resolveid
|
||||
return resolvedFile || null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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 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' || options?.debug) {
|
||||
console.debug('\n[Nx Vite TsPaths]', ...msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function logIt(...msg: any[]) {
|
||||
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
console.debug('[Nx Vite TsPaths]', ...msg);
|
||||
function loadFileFromPaths(
|
||||
tsconfig: ConfigLoaderSuccessResult,
|
||||
importPath: string
|
||||
) {
|
||||
logIt(
|
||||
`Trying to resolve file from config in ${tsconfig.configFileAbsolutePath}`
|
||||
);
|
||||
let resolvedFile: string;
|
||||
for (const alias in tsconfig.paths) {
|
||||
const paths = tsconfig.paths[alias];
|
||||
|
||||
const normalizedImport = alias.replace(/\/\*$/, '');
|
||||
|
||||
if (importPath.startsWith(normalizedImport)) {
|
||||
const path = (tsconfig.absoluteBaseUrl, paths[0].replace(/\/\*$/, ''));
|
||||
resolvedFile = findFile(importPath.replace(normalizedImport, path));
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedFile;
|
||||
}
|
||||
|
||||
function findFile(path: string): string {
|
||||
for (const ext of options.extensions) {
|
||||
const r = resolve(path + ext);
|
||||
if (existsSync(r)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user