From 42e247ccd835baaa58d6c122590286041da0672e Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 19 Mar 2020 16:32:56 +0100 Subject: [PATCH 1/3] Add support for baseUrl option in tsconfig and jsconfig --- packages/next/build/webpack-config.ts | 23 +++++++++++++- .../jsconfig-baseurl/components/world.js | 5 +++ .../jsconfig-baseurl/jsconfig.json | 5 +++ .../jsconfig-baseurl/next.config.js | 6 ++++ .../jsconfig-baseurl/pages/hello.js | 9 ++++++ .../jsconfig-baseurl/test/index.test.js | 31 +++++++++++++++++++ .../typescript-baseurl/components/world.tsx | 5 +++ .../typescript-baseurl/next.config.js | 6 ++++ .../typescript-baseurl/pages/hello.tsx | 9 ++++++ .../typescript-baseurl/test/index.test.js | 31 +++++++++++++++++++ .../typescript-baseurl/tsconfig.json | 20 ++++++++++++ 11 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 test/integration/jsconfig-baseurl/components/world.js create mode 100644 test/integration/jsconfig-baseurl/jsconfig.json create mode 100644 test/integration/jsconfig-baseurl/next.config.js create mode 100644 test/integration/jsconfig-baseurl/pages/hello.js create mode 100644 test/integration/jsconfig-baseurl/test/index.test.js create mode 100644 test/integration/typescript-baseurl/components/world.tsx create mode 100644 test/integration/typescript-baseurl/next.config.js create mode 100644 test/integration/typescript-baseurl/pages/hello.tsx create mode 100644 test/integration/typescript-baseurl/test/index.test.js create mode 100644 test/integration/typescript-baseurl/tsconfig.json diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index d4e3106196e3c6e..e13fefb50aada5c 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -217,6 +217,22 @@ export default async function getBaseWebpackConfig( ? config.typescript?.ignoreDevErrors : config.typescript?.ignoreBuildErrors + const jsConfigPath = path.join(dir, 'jsconfig.json') + let jsConfig + // jsconfig is a subset of tsconfig + if (useTypeScript) { + jsConfig = require(tsConfigPath) + } + + if (!useTypeScript && (await fileExists(jsConfigPath))) { + jsConfig = require(jsConfigPath) + } + + let resolvedBaseUrl + if (jsConfig?.compilerOptions?.baseUrl) { + resolvedBaseUrl = path.resolve(dir, jsConfig.compilerOptions.baseUrl) + } + const resolveConfig = { // Disable .mjs for node_modules bundling extensions: isServer @@ -239,7 +255,7 @@ export default async function getBaseWebpackConfig( modules: [ 'node_modules', ...nodePathList, // Support for NODE_PATH environment variable - ], + ].filter(Boolean), alias: { // These aliases make sure the wrapper module is not included in the bundles // Which makes bundles slightly smaller, but also skips parsing a module that we know will result in this alias @@ -889,6 +905,11 @@ export default async function getBaseWebpackConfig( ].filter((Boolean as any) as ExcludesFalse), } + // Support tsconfig and jsconfig baseUrl + if (resolvedBaseUrl) { + webpackConfig.resolve?.modules?.push(resolvedBaseUrl) + } + webpackConfig = await buildConfiguration(webpackConfig, { rootDirectory: dir, customAppFile, diff --git a/test/integration/jsconfig-baseurl/components/world.js b/test/integration/jsconfig-baseurl/components/world.js new file mode 100644 index 000000000000000..5a93b9838685c12 --- /dev/null +++ b/test/integration/jsconfig-baseurl/components/world.js @@ -0,0 +1,5 @@ +import React from 'react' + +export function World() { + return <>World +} diff --git a/test/integration/jsconfig-baseurl/jsconfig.json b/test/integration/jsconfig-baseurl/jsconfig.json new file mode 100644 index 000000000000000..36aa1a4dc28f1a7 --- /dev/null +++ b/test/integration/jsconfig-baseurl/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/test/integration/jsconfig-baseurl/next.config.js b/test/integration/jsconfig-baseurl/next.config.js new file mode 100644 index 000000000000000..cc17cf48c578fd5 --- /dev/null +++ b/test/integration/jsconfig-baseurl/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, + }, +} diff --git a/test/integration/jsconfig-baseurl/pages/hello.js b/test/integration/jsconfig-baseurl/pages/hello.js new file mode 100644 index 000000000000000..430fdc247a8646f --- /dev/null +++ b/test/integration/jsconfig-baseurl/pages/hello.js @@ -0,0 +1,9 @@ +import React from 'react' +import { World } from 'components/world' +export default function HelloPage() { + return ( +
+ +
+ ) +} diff --git a/test/integration/jsconfig-baseurl/test/index.test.js b/test/integration/jsconfig-baseurl/test/index.test.js new file mode 100644 index 000000000000000..ec516f7766ac875 --- /dev/null +++ b/test/integration/jsconfig-baseurl/test/index.test.js @@ -0,0 +1,31 @@ +/* eslint-env jest */ +/* global jasmine */ +import { join } from 'path' +import cheerio from 'cheerio' +import { renderViaHTTP, findPort, launchApp, killApp } from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 + +const appDir = join(__dirname, '..') +let appPort +let app + +async function get$(path, query) { + const html = await renderViaHTTP(appPort, path, query) + return cheerio.load(html) +} + +describe('TypeScript Features', () => { + describe('default behavior', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort, {}) + }) + afterAll(() => killApp(app)) + + it('should render the page', async () => { + const $ = await get$('/hello') + expect($('body').text()).toMatch(/World/) + }) + }) +}) diff --git a/test/integration/typescript-baseurl/components/world.tsx b/test/integration/typescript-baseurl/components/world.tsx new file mode 100644 index 000000000000000..d7d1f66258c778e --- /dev/null +++ b/test/integration/typescript-baseurl/components/world.tsx @@ -0,0 +1,5 @@ +import React from 'react' + +export function World(): JSX.Element { + return <>World +} diff --git a/test/integration/typescript-baseurl/next.config.js b/test/integration/typescript-baseurl/next.config.js new file mode 100644 index 000000000000000..cc17cf48c578fd5 --- /dev/null +++ b/test/integration/typescript-baseurl/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, + }, +} diff --git a/test/integration/typescript-baseurl/pages/hello.tsx b/test/integration/typescript-baseurl/pages/hello.tsx new file mode 100644 index 000000000000000..1129fe708833b25 --- /dev/null +++ b/test/integration/typescript-baseurl/pages/hello.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { World } from 'components/world' +export default function HelloPage(): JSX.Element { + return ( +
+ +
+ ) +} diff --git a/test/integration/typescript-baseurl/test/index.test.js b/test/integration/typescript-baseurl/test/index.test.js new file mode 100644 index 000000000000000..ec516f7766ac875 --- /dev/null +++ b/test/integration/typescript-baseurl/test/index.test.js @@ -0,0 +1,31 @@ +/* eslint-env jest */ +/* global jasmine */ +import { join } from 'path' +import cheerio from 'cheerio' +import { renderViaHTTP, findPort, launchApp, killApp } from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 + +const appDir = join(__dirname, '..') +let appPort +let app + +async function get$(path, query) { + const html = await renderViaHTTP(appPort, path, query) + return cheerio.load(html) +} + +describe('TypeScript Features', () => { + describe('default behavior', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort, {}) + }) + afterAll(() => killApp(app)) + + it('should render the page', async () => { + const $ = await get$('/hello') + expect($('body').text()).toMatch(/World/) + }) + }) +}) diff --git a/test/integration/typescript-baseurl/tsconfig.json b/test/integration/typescript-baseurl/tsconfig.json new file mode 100644 index 000000000000000..dce2837cb1bccbd --- /dev/null +++ b/test/integration/typescript-baseurl/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "esModuleInterop": true, + "module": "esnext", + "jsx": "preserve", + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true + }, + "exclude": ["node_modules"], + "include": ["next-env.d.ts", "components", "pages"] +} From d55ccb34911c373fc67d22eed6f799fe3382a51a Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 19 Mar 2020 16:35:57 +0100 Subject: [PATCH 2/3] Move jsconfigPath --- packages/next/build/webpack-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index e13fefb50aada5c..8fd513b58b948c5 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -217,13 +217,13 @@ export default async function getBaseWebpackConfig( ? config.typescript?.ignoreDevErrors : config.typescript?.ignoreBuildErrors - const jsConfigPath = path.join(dir, 'jsconfig.json') let jsConfig // jsconfig is a subset of tsconfig if (useTypeScript) { jsConfig = require(tsConfigPath) } + const jsConfigPath = path.join(dir, 'jsconfig.json') if (!useTypeScript && (await fileExists(jsConfigPath))) { jsConfig = require(jsConfigPath) } From a15f0d84a49739254aae849a46b7142683fe46b1 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 19 Mar 2020 16:36:23 +0100 Subject: [PATCH 3/3] Remove filter --- packages/next/build/webpack-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 8fd513b58b948c5..d3b2d2551b896ab 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -255,7 +255,7 @@ export default async function getBaseWebpackConfig( modules: [ 'node_modules', ...nodePathList, // Support for NODE_PATH environment variable - ].filter(Boolean), + ], alias: { // These aliases make sure the wrapper module is not included in the bundles // Which makes bundles slightly smaller, but also skips parsing a module that we know will result in this alias