fix(nextjs): add extension alias support for handling ESM libs (#31323)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> Currently, if you try to import a ESM lib after you generate a Next.js application it fails to build due to how the module exports `export * from './lib/lib8446520.js';`. This has been addressed with webpack and needs to be extended to Next.js. ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> You should work out of the box and you should be able to import a lib defined like: `export * from './lib/lib8446520.js';.` ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> This is also related to our webpack and rspack packages. Changes have also been made to them to ensure consistency across bundlers. Fixes #30714
This commit is contained in:
parent
5c405fa72f
commit
ae89efb8d1
63
e2e/next/src/next-ts-solutions.test.ts
Normal file
63
e2e/next/src/next-ts-solutions.test.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
cleanupProject,
|
||||
getPackageManagerCommand,
|
||||
newProject,
|
||||
readFile,
|
||||
runCLI,
|
||||
runCommand,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
} from '@nx/e2e/utils';
|
||||
describe('Next TS Solutions', () => {
|
||||
let proj: string;
|
||||
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/next'],
|
||||
preset: 'ts',
|
||||
});
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
it('should support importing a esm library', async () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --linter=none --unitTestRunner=none --e2eTestRunner=none`
|
||||
);
|
||||
|
||||
runCLI(
|
||||
`generate @nx/js:lib packages/${libName} --bundler=vite --no-interactive --unit-test-runner=none --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`${appName}/src/app/page.tsx`,
|
||||
`
|
||||
import {${libName}} from '@${proj}/${libName}';
|
||||
${readFile(`${appName}/src/app/page.tsx`)}
|
||||
console.log(${libName}());
|
||||
`
|
||||
);
|
||||
runCLI('sync');
|
||||
|
||||
// Add library to package.json to make sure it is linked (not needed for npm package manager)
|
||||
updateJson(`${appName}/package.json`, (json) => {
|
||||
return {
|
||||
...json,
|
||||
devDependencies: {
|
||||
...(json.devDependencies || {}),
|
||||
[`@${proj}/${libName}`]: 'workspace:*',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
runCommand(`cd ${appName} && ${getPackageManagerCommand().install}`);
|
||||
|
||||
const output = runCLI(`build ${appName}`);
|
||||
expect(output).toContain(
|
||||
`Successfully ran target build for project @${proj}/${appName}`
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -10,7 +10,6 @@ import {
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import * as http from 'http';
|
||||
import { checkApp } from './utils';
|
||||
|
||||
describe('Next.js Applications', () => {
|
||||
@ -240,20 +239,3 @@ describe('Next.js Applications', () => {
|
||||
expect(postBuildPagesContent).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
function getData(port, path = ''): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
http
|
||||
.get(`http://localhost:${port}${path}`, (res) => {
|
||||
expect(res.statusCode).toEqual(200);
|
||||
let data = '';
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
res.once('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
})
|
||||
.on('error', (err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
@ -87,4 +87,47 @@ describe('React (TS solution)', () => {
|
||||
|
||||
checkFilesExist(`packages/${appName}/dist/index.html`);
|
||||
}, 90_000);
|
||||
|
||||
it('should be able to use Rspack to build apps with an imported lib', async () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app packages/${appName} --bundler=rspack --no-interactive --skipFormat --linter=eslint --unitTestRunner=none`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${libName} --bundler=none --no-interactive --unit-test-runner=none --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
const mainPath = `packages/${appName}/src/main.tsx`;
|
||||
updateFile(
|
||||
mainPath,
|
||||
`
|
||||
import {${libName}} from '@${workspaceName}/${libName}';
|
||||
${readFile(mainPath)}
|
||||
console.log(${libName}());
|
||||
`
|
||||
);
|
||||
|
||||
runCLI('sync');
|
||||
|
||||
// Add library to package.json to make sure it is linked (not needed for npm package manager)
|
||||
updateJson(`packages/${appName}/package.json`, (json) => {
|
||||
return {
|
||||
...json,
|
||||
devDependencies: {
|
||||
...(json.devDependencies || {}),
|
||||
[`@${workspaceName}/${libName}`]: 'workspace:*',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
runCommand(
|
||||
`cd packages/${appName} && ${getPackageManagerCommand().install}`
|
||||
);
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
|
||||
checkFilesExist(`packages/${appName}/dist/index.html`);
|
||||
}, 90_000);
|
||||
});
|
||||
|
||||
@ -261,6 +261,17 @@ export function getNextConfig(
|
||||
},
|
||||
...validNextConfig,
|
||||
webpack: (config, options) => {
|
||||
/**
|
||||
* To support ESM library export, we need to ensure the extensionAlias contains both `.js` and `.ts` extensions.
|
||||
* This is because Webpack uses the `extensionAlias` to resolve the correct file extension when importing modules.
|
||||
*/
|
||||
config.resolve.extensionAlias = {
|
||||
...(config.resolve.extensionAlias || {}),
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
'.jsx': ['.tsx', '.jsx'],
|
||||
};
|
||||
/*
|
||||
* Update babel to support our monorepo setup.
|
||||
* The 'upward' mode allows the root babel.config.json and per-project .babelrc files to be picked up.
|
||||
|
||||
@ -27,6 +27,13 @@ const IGNORED_RSPACK_WARNINGS = [
|
||||
];
|
||||
|
||||
const extensions = ['...', '.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||
|
||||
const extensionAlias = {
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
'.jsx': ['.tsx', '.jsx'],
|
||||
};
|
||||
const mainFields = ['module', 'main'];
|
||||
|
||||
export function applyBaseConfig(
|
||||
@ -385,6 +392,10 @@ function applyNxDependentConfig(
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
extensions: [...(config?.resolve?.extensions ?? []), ...extensions],
|
||||
extensionAlias: {
|
||||
...(config.resolve?.extensionAlias ?? {}),
|
||||
...extensionAlias,
|
||||
},
|
||||
alias: {
|
||||
...(config.resolve?.alias ?? {}),
|
||||
...(options.fileReplacements?.reduce(
|
||||
|
||||
@ -28,8 +28,10 @@ const IGNORED_WEBPACK_WARNINGS = [
|
||||
];
|
||||
|
||||
const extensionAlias = {
|
||||
'.js': ['.ts', '.js'],
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
'.jsx': ['.tsx', '.jsx'],
|
||||
};
|
||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||
const mainFields = ['module', 'main'];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user