diff --git a/lerna.json b/lerna.json index ab7a5073a97ca..08da7a271818d 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "13.5.4-canary.4" + "version": "13.5.4-canary.5" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 6c6f18bf2e71b..376986f0299d5 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index add6cc214855a..a6f1334ce5da7 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,7 +10,7 @@ }, "homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config", "dependencies": { - "@next/eslint-plugin-next": "13.5.4-canary.4", + "@next/eslint-plugin-next": "13.5.4-canary.5", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", "eslint-import-resolver-node": "^0.3.6", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index f89e4dad22a34..01acd830053e9 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 52459ed801f8b..9552972b2aa5f 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index e2ebff2af8072..ac04e1a010550 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 636f66725f16a..57c97b1fa3dc7 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 14695d2998a5b..133842a2ced7a 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index db3461aa44701..6e489124831db 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 7cc4827f83387..333861022293f 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 3a64efea70ae6..d92bcaca17ea4 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 6620ccade13c4..13598535ee14a 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/crates/next-core/src/env.rs b/packages/next-swc/crates/next-core/src/env.rs index 9fff7bea98c63..0c44138c27915 100644 --- a/packages/next-swc/crates/next-core/src/env.rs +++ b/packages/next-swc/crates/next-core/src/env.rs @@ -99,6 +99,46 @@ pub async fn env_for_js( }, ); + map.insert( + "__NEXT_MANUAL_TRAILING_SLASH".to_string(), + if next_config.skip_trailing_slash_redirect.unwrap_or(false) { + "true".to_string() + } else { + "false".to_string() + }, + ); + + map.insert( + "__NEXT_NO_MIDDLEWARE_URL_NORMALIZE".to_string(), + if next_config.skip_middleware_url_normalize.unwrap_or(false) { + "true".to_string() + } else { + "false".to_string() + }, + ); + + map.insert( + "__NEXT_EXTERNAL_MIDDLEWARE_REWRITE_RESOLVE".to_string(), + if next_config + .experimental + .external_middleware_rewrites_resolve + .unwrap_or(false) + { + "true".to_string() + } else { + "false".to_string() + }, + ); + + map.insert( + "__NEXT_SCROLL_RESTORATION".to_string(), + if next_config.experimental.scroll_restoration.unwrap_or(false) { + "true".to_string() + } else { + "false".to_string() + }, + ); + if !test_mode.is_empty() { map.insert("__NEXT_TEST_MODE".to_string(), "true".to_string()); } diff --git a/packages/next-swc/crates/next-core/src/next_config.rs b/packages/next-swc/crates/next-core/src/next_config.rs index 7bfcd97e488dc..619483aaddbe4 100644 --- a/packages/next-swc/crates/next-core/src/next_config.rs +++ b/packages/next-swc/crates/next-core/src/next_config.rs @@ -88,6 +88,8 @@ pub struct NextConfig { pub trailing_slash: Option, pub asset_prefix: Option, pub base_path: Option, + pub skip_middleware_url_normalize: Option, + pub skip_trailing_slash_redirect: Option, /// #[serde(rename = "_originalRedirects")] @@ -127,8 +129,6 @@ pub struct NextConfig { typescript: TypeScriptConfig, use_file_system_public_routes: bool, webpack: Option, - skip_middleware_url_normalize: Option, - skip_trailing_slash_redirect: Option, } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs)] @@ -429,6 +429,8 @@ pub struct ExperimentalConfig { pub swc_plugins: Option>, pub turbo: Option, pub turbotrace: Option, + pub external_middleware_rewrites_resolve: Option, + pub scroll_restoration: Option, // --- // UNSUPPORTED @@ -446,7 +448,6 @@ pub struct ExperimentalConfig { esm_externals: Option, extension_alias: Option, external_dir: Option, - external_middleware_rewrites_resolve: Option, /// If set to `false`, webpack won't fall back to polyfill Node.js modules /// in the browser Full list of old polyfills is accessible here: /// [webpack/webpack#Module_notound_error.js#L13-L42](https://github.com/webpack/webpack/blob/2a0536cf510768111a3a6dceeb14cb79b9f59273/lib/Module_not_found_error.js#L13-L42) @@ -480,7 +481,6 @@ pub struct ExperimentalConfig { /// directory. ppr: Option, proxy_timeout: Option, - scroll_restoration: Option, /// Enables server actions. Using this feature will enable the /// `react@experimental` for the `app` directory. @see https://nextjs.org/docs/app/api-reference/functions/server-actions server_actions: Option, diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index f91f3445f928a..f28452a53c0e6 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index f35c4ed9aae72..9e68c78a0d1e1 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -90,7 +90,7 @@ ] }, "dependencies": { - "@next/env": "13.5.4-canary.4", + "@next/env": "13.5.4-canary.5", "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -144,11 +144,11 @@ "@mswjs/interceptors": "0.23.0", "@napi-rs/cli": "2.16.2", "@napi-rs/triples": "1.1.0", - "@next/polyfill-module": "13.5.4-canary.4", - "@next/polyfill-nomodule": "13.5.4-canary.4", - "@next/react-dev-overlay": "13.5.4-canary.4", - "@next/react-refresh-utils": "13.5.4-canary.4", - "@next/swc": "13.5.4-canary.4", + "@next/polyfill-module": "13.5.4-canary.5", + "@next/polyfill-nomodule": "13.5.4-canary.5", + "@next/react-dev-overlay": "13.5.4-canary.5", + "@next/react-refresh-utils": "13.5.4-canary.5", + "@next/swc": "13.5.4-canary.5", "@opentelemetry/api": "1.4.1", "@playwright/test": "^1.35.1", "@segment/ajv-human-errors": "2.1.2", diff --git a/packages/next/src/build/handle-externals.ts b/packages/next/src/build/handle-externals.ts new file mode 100644 index 0000000000000..36d203f1f6981 --- /dev/null +++ b/packages/next/src/build/handle-externals.ts @@ -0,0 +1,400 @@ +import { WebpackLayerName, WEBPACK_LAYERS } from '../lib/constants' +import { defaultOverrides } from '../server/require-hook' +import { BARREL_OPTIMIZATION_PREFIX } from '../shared/lib/constants' +import path from '../shared/lib/isomorphic/path' +import { + NODE_BASE_ESM_RESOLVE_OPTIONS, + NODE_BASE_RESOLVE_OPTIONS, + NODE_ESM_RESOLVE_OPTIONS, + NODE_RESOLVE_OPTIONS, +} from './webpack-config' +import { isWebpackAppLayer, isWebpackServerLayer } from './worker' +import { NextConfigComplete } from '../server/config-shared' +const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/ + +const pathSeparators = '[/\\\\]' +const optionalEsmPart = `((${pathSeparators}esm)?${pathSeparators})` +const externalFileEnd = '(\\.external(\\.js)?)$' +const nextDist = `next${pathSeparators}dist` + +const externalPattern = new RegExp( + `${nextDist}${optionalEsmPart}.*${externalFileEnd}` +) + +export function isResourceInPackages( + resource: string, + packageNames?: string[], + packageDirMapping?: Map +) { + return packageNames?.some((p: string) => + packageDirMapping && packageDirMapping.has(p) + ? resource.startsWith(packageDirMapping.get(p)! + path.sep) + : resource.includes( + path.sep + + path.join('node_modules', p.replace(/\//g, path.sep)) + + path.sep + ) + ) +} + +export async function resolveExternal( + dir: string, + esmExternalsConfig: NextConfigComplete['experimental']['esmExternals'], + context: string, + request: string, + isEsmRequested: boolean, + hasAppDir: boolean, + getResolve: ( + options: any + ) => ( + resolveContext: string, + resolveRequest: string + ) => Promise<[string | null, boolean]>, + isLocalCallback?: (res: string) => any, + baseResolveCheck = true, + esmResolveOptions: any = NODE_ESM_RESOLVE_OPTIONS, + nodeResolveOptions: any = NODE_RESOLVE_OPTIONS, + baseEsmResolveOptions: any = NODE_BASE_ESM_RESOLVE_OPTIONS, + baseResolveOptions: any = NODE_BASE_RESOLVE_OPTIONS +) { + const esmExternals = !!esmExternalsConfig + const looseEsmExternals = esmExternalsConfig === 'loose' + + let res: string | null = null + let isEsm: boolean = false + + let preferEsmOptions = + esmExternals && isEsmRequested ? [true, false] : [false] + // Disable esm resolving for app/ and pages/ so for esm package using under pages/ + // won't load react through esm loader + if (hasAppDir) { + preferEsmOptions = [false] + } + for (const preferEsm of preferEsmOptions) { + const resolve = getResolve( + preferEsm ? esmResolveOptions : nodeResolveOptions + ) + + // Resolve the import with the webpack provided context, this + // ensures we're resolving the correct version when multiple + // exist. + try { + ;[res, isEsm] = await resolve(context, request) + } catch (err) { + res = null + } + + if (!res) { + continue + } + + // ESM externals can only be imported (and not required). + // Make an exception in loose mode. + if (!isEsmRequested && isEsm && !looseEsmExternals) { + continue + } + + if (isLocalCallback) { + return { localRes: isLocalCallback(res) } + } + + // Bundled Node.js code is relocated without its node_modules tree. + // This means we need to make sure its request resolves to the same + // package that'll be available at runtime. If it's not identical, + // we need to bundle the code (even if it _should_ be external). + if (baseResolveCheck) { + let baseRes: string | null + let baseIsEsm: boolean + try { + const baseResolve = getResolve( + isEsm ? baseEsmResolveOptions : baseResolveOptions + ) + ;[baseRes, baseIsEsm] = await baseResolve(dir, request) + } catch (err) { + baseRes = null + baseIsEsm = false + } + + // Same as above: if the package, when required from the root, + // would be different from what the real resolution would use, we + // cannot externalize it. + // if request is pointing to a symlink it could point to the the same file, + // the resolver will resolve symlinks so this is handled + if (baseRes !== res || isEsm !== baseIsEsm) { + res = null + continue + } + } + break + } + return { res, isEsm } +} + +export function makeExternalHandler({ + config, + optOutBundlingPackageRegex, + dir, + hasAppDir, +}: { + config: NextConfigComplete + optOutBundlingPackageRegex: RegExp + dir: string + hasAppDir: boolean +}) { + let resolvedExternalPackageDirs: Map + const looseEsmExternals = config.experimental?.esmExternals === 'loose' + + return async function handleExternals( + context: string, + request: string, + dependencyType: string, + layer: WebpackLayerName | null, + getResolve: ( + options: any + ) => ( + resolveContext: string, + resolveRequest: string + ) => Promise<[string | null, boolean]> + ) { + // We need to externalize internal requests for files intended to + // not be bundled. + const isLocal: boolean = + request.startsWith('.') || + // Always check for unix-style path, as webpack sometimes + // normalizes as posix. + path.posix.isAbsolute(request) || + // When on Windows, we also want to check for Windows-specific + // absolute paths. + (process.platform === 'win32' && path.win32.isAbsolute(request)) + + // make sure import "next" shows a warning when imported + // in pages/components + if (request === 'next') { + return `commonjs next/dist/lib/import-next-warning` + } + + const isAppLayer = isWebpackAppLayer(layer) + + // Relative requires don't need custom resolution, because they + // are relative to requests we've already resolved here. + // Absolute requires (require('/foo')) are extremely uncommon, but + // also have no need for customization as they're already resolved. + if (!isLocal) { + if (/^(?:next$)/.test(request)) { + return `commonjs ${request}` + } + + if (reactPackagesRegex.test(request) && !isAppLayer) { + return `commonjs ${request}` + } + + const notExternalModules = + /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers|router)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ + if (notExternalModules.test(request)) { + return + } + } + + // @swc/helpers should not be external as it would + // require hoisting the package which we can't rely on + if (request.includes('@swc/helpers')) { + return + } + + // BARREL_OPTIMIZATION_PREFIX is a special marker that tells Next.js to + // optimize the import by removing unused exports. This has to be compiled. + if (request.startsWith(BARREL_OPTIMIZATION_PREFIX)) { + return + } + + // When in esm externals mode, and using import, we resolve with + // ESM resolving options. + // Also disable esm request when appDir is enabled + const isEsmRequested = dependencyType === 'esm' + + /** + * @param localRes the full path to the file + * @returns the externalized path + * @description returns an externalized path if the file is a Next.js file and ends with either `.shared-runtime.js` or `.external.js` + * This is used to ensure that files used across the rendering runtime(s) and the user code are one and the same. The logic in this function + * will rewrite the require to the correct bundle location depending on the layer at which the file is being used. + */ + const resolveNextExternal = (localRes: string) => { + const isExternal = externalPattern.test(localRes) + + // if the file ends with .external, we need to make it a commonjs require in all cases + // this is used mainly to share the async local storage across the routing, rendering and user layers. + if (isExternal) { + // it's important we return the path that starts with `next/dist/` here instead of the absolute path + // otherwise NFT will get tripped up + return `commonjs ${localRes.replace(/.*?next[/\\]dist/, 'next/dist')}` + } + } + + // Don't bundle @vercel/og nodejs bundle for nodejs runtime. + // TODO-APP: bundle route.js with different layer that externals common node_module deps. + if ( + isWebpackServerLayer(layer) && + request === 'next/dist/compiled/@vercel/og/index.node.js' + ) { + return `module ${request}` + } + + // Specific Next.js imports that should remain external + // TODO-APP: Investigate if we can remove this. + if (request.startsWith('next/dist/')) { + // Non external that needs to be transpiled + // Image loader needs to be transpiled + if (/^next[\\/]dist[\\/]shared[\\/]lib[\\/]image-loader/.test(request)) { + return + } + + if (/^next[\\/]dist[\\/]compiled[\\/]next-server/.test(request)) { + return `commonjs ${request}` + } + + if ( + /^next[\\/]dist[\\/]shared[\\/](?!lib[\\/]router[\\/]router)/.test( + request + ) || + /^next[\\/]dist[\\/]compiled[\\/].*\.c?js$/.test(request) + ) { + return `commonjs ${request}` + } + + if ( + /^next[\\/]dist[\\/]esm[\\/]shared[\\/](?!lib[\\/]router[\\/]router)/.test( + request + ) || + /^next[\\/]dist[\\/]compiled[\\/].*\.mjs$/.test(request) + ) { + return `module ${request}` + } + + return resolveNextExternal(request) + } + + // Early return if the request needs to be bundled, such as in the client layer. + // Treat react packages and next internals as external for SSR layer, + // also map react to builtin ones with require-hook. + if (layer === WEBPACK_LAYERS.serverSideRendering) { + const isRelative = request.startsWith('.') + const fullRequest = isRelative + ? path.join(context, request).replace(/\\/g, '/') + : request + return resolveNextExternal(fullRequest) + } + + // TODO-APP: Let's avoid this resolve call as much as possible, and eventually get rid of it. + const resolveResult = await resolveExternal( + dir, + config.experimental.esmExternals, + context, + request, + isEsmRequested, + hasAppDir, + getResolve, + isLocal ? resolveNextExternal : undefined + ) + + if ('localRes' in resolveResult) { + return resolveResult.localRes + } + + // Forcedly resolve the styled-jsx installed by next.js, + // since `resolveExternal` cannot find the styled-jsx dep with pnpm + if (request === 'styled-jsx/style') { + resolveResult.res = defaultOverrides['styled-jsx/style'] + } + + const { res, isEsm } = resolveResult + + // If the request cannot be resolved we need to have + // webpack "bundle" it so it surfaces the not found error. + if (!res) { + return + } + + // ESM externals can only be imported (and not required). + // Make an exception in loose mode. + if (!isEsmRequested && isEsm && !looseEsmExternals) { + throw new Error( + `ESM packages (${request}) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals` + ) + } + + const externalType = isEsm ? 'module' : 'commonjs' + + // Default pages have to be transpiled + if ( + // This is the @babel/plugin-transform-runtime "helpers: true" option + /node_modules[/\\]@babel[/\\]runtime[/\\]/.test(res) + ) { + return + } + + // Webpack itself has to be compiled because it doesn't always use module relative paths + if ( + /node_modules[/\\]webpack/.test(res) || + /node_modules[/\\]css-loader/.test(res) + ) { + return + } + + // If a package should be transpiled by Next.js, we skip making it external. + // It doesn't matter what the extension is, as we'll transpile it anyway. + if (config.transpilePackages && !resolvedExternalPackageDirs) { + resolvedExternalPackageDirs = new Map() + // We need to resolve all the external package dirs initially. + for (const pkg of config.transpilePackages) { + const pkgRes = await resolveExternal( + dir, + config.experimental.esmExternals, + context, + pkg + '/package.json', + hasAppDir, + isEsmRequested, + getResolve, + isLocal ? resolveNextExternal : undefined + ) + if (pkgRes.res) { + resolvedExternalPackageDirs.set(pkg, path.dirname(pkgRes.res)) + } + } + } + + // If a package is included in `transpilePackages`, we don't want to make it external. + // And also, if that resource is an ES module, we bundle it too because we can't + // rely on the require hook to alias `react` to our precompiled version. + const shouldBeBundled = + isResourceInPackages( + res, + config.transpilePackages, + resolvedExternalPackageDirs + ) || + (isEsm && isAppLayer) + + if (/node_modules[/\\].*\.[mc]?js$/.test(res)) { + if (isWebpackServerLayer(layer)) { + // All packages should be bundled for the server layer if they're not opted out. + // This option takes priority over the transpilePackages option. + + if (optOutBundlingPackageRegex.test(res)) { + return `${externalType} ${request}` + } + + return + } + + if (shouldBeBundled) return + + // Anything else that is standard JavaScript within `node_modules` + // can be externalized. + return `${externalType} ${request}` + } + + if (shouldBeBundled) return + + // Default behavior: bundle the code! + } +} diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index 8c13fe212bd40..e67191f0cf1b1 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -19,11 +19,7 @@ import { WEBPACK_RESOURCE_QUERIES, WebpackLayerName, } from '../lib/constants' -import { - isWebpackAppLayer, - isWebpackDefaultLayer, - isWebpackServerLayer, -} from './utils' +import { isWebpackDefaultLayer, isWebpackServerLayer } from './utils' import { CustomRoutes } from '../lib/load-custom-routes.js' import { CLIENT_STATIC_FILES_RUNTIME_AMP, @@ -37,7 +33,6 @@ import { SERVER_DIRECTORY, COMPILER_NAMES, CompilerNameValues, - BARREL_OPTIMIZATION_PREFIX, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' @@ -78,6 +73,7 @@ import { defaultOverrides } from '../server/require-hook' import { needsExperimentalReact } from '../lib/needs-experimental-react' import { getDefineEnvPlugin } from './webpack/plugins/define-env-plugin' import { SWCLoaderOptions } from './webpack/loaders/next-swc-loader' +import { isResourceInPackages, makeExternalHandler } from './handle-externals' type ExcludesFalse = (x: T | false) => x is T type ClientEntries = { @@ -105,20 +101,9 @@ const babelIncludeRegexes: RegExp[] = [ /[\\/](strip-ansi|ansi-regex|styled-jsx)[\\/]/, ] -const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/ - const asyncStoragesRegex = /next[\\/]dist[\\/](esm[\\/])?client[\\/]components[\\/](static-generation-async-storage|action-async-storage|request-async-storage)/ -const pathSeparators = '[/\\\\]' -const optionalEsmPart = `((${pathSeparators}esm)?${pathSeparators})` -const externalFileEnd = '(\\.external(\\.js)?)$' -const nextDist = `next${pathSeparators}dist` - -const externalPattern = new RegExp( - `${nextDist}${optionalEsmPart}.*${externalFileEnd}` -) - // exports. const edgeConditionNames = [ 'edge-light', @@ -168,22 +153,6 @@ export function errorIfEnvConflicted(config: NextConfigComplete, key: string) { } } -function isResourceInPackages( - resource: string, - packageNames?: string[], - packageDirMapping?: Map -) { - return packageNames?.some((p: string) => - packageDirMapping && packageDirMapping.has(p) - ? resource.startsWith(packageDirMapping.get(p)! + path.sep) - : resource.includes( - path.sep + - path.join('node_modules', p.replace(/\//g, path.sep)) + - path.sep - ) - ) -} - function getReactProfilingInProduction() { return { 'react-dom$': 'react-dom/profiling', @@ -415,99 +384,6 @@ export const NODE_BASE_ESM_RESOLVE_OPTIONS = { export const nextImageLoaderRegex = /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i -export async function resolveExternal( - dir: string, - esmExternalsConfig: NextConfigComplete['experimental']['esmExternals'], - context: string, - request: string, - isEsmRequested: boolean, - hasAppDir: boolean, - getResolve: ( - options: any - ) => ( - resolveContext: string, - resolveRequest: string - ) => Promise<[string | null, boolean]>, - isLocalCallback?: (res: string) => any, - baseResolveCheck = true, - esmResolveOptions: any = NODE_ESM_RESOLVE_OPTIONS, - nodeResolveOptions: any = NODE_RESOLVE_OPTIONS, - baseEsmResolveOptions: any = NODE_BASE_ESM_RESOLVE_OPTIONS, - baseResolveOptions: any = NODE_BASE_RESOLVE_OPTIONS -) { - const esmExternals = !!esmExternalsConfig - const looseEsmExternals = esmExternalsConfig === 'loose' - - let res: string | null = null - let isEsm: boolean = false - - let preferEsmOptions = - esmExternals && isEsmRequested ? [true, false] : [false] - // Disable esm resolving for app/ and pages/ so for esm package using under pages/ - // won't load react through esm loader - if (hasAppDir) { - preferEsmOptions = [false] - } - for (const preferEsm of preferEsmOptions) { - const resolve = getResolve( - preferEsm ? esmResolveOptions : nodeResolveOptions - ) - - // Resolve the import with the webpack provided context, this - // ensures we're resolving the correct version when multiple - // exist. - try { - ;[res, isEsm] = await resolve(context, request) - } catch (err) { - res = null - } - - if (!res) { - continue - } - - // ESM externals can only be imported (and not required). - // Make an exception in loose mode. - if (!isEsmRequested && isEsm && !looseEsmExternals) { - continue - } - - if (isLocalCallback) { - return { localRes: isLocalCallback(res) } - } - - // Bundled Node.js code is relocated without its node_modules tree. - // This means we need to make sure its request resolves to the same - // package that'll be available at runtime. If it's not identical, - // we need to bundle the code (even if it _should_ be external). - if (baseResolveCheck) { - let baseRes: string | null - let baseIsEsm: boolean - try { - const baseResolve = getResolve( - isEsm ? baseEsmResolveOptions : baseResolveOptions - ) - ;[baseRes, baseIsEsm] = await baseResolve(dir, request) - } catch (err) { - baseRes = null - baseIsEsm = false - } - - // Same as above: if the package, when required from the root, - // would be different from what the real resolution would use, we - // cannot externalize it. - // if request is pointing to a symlink it could point to the the same file, - // the resolver will resolve symlinks so this is handled - if (baseRes !== res || isEsm !== baseIsEsm) { - res = null - continue - } - } - break - } - return { res, isEsm } -} - export async function loadProjectInfo({ dir, config, @@ -1147,7 +1023,6 @@ export default async function getBaseWebpackConfig( } const crossOrigin = config.crossOrigin - const looseEsmExternals = config.experimental?.esmExternals === 'loose' const optOutBundlingPackages = EXTERNAL_PACKAGES.concat( ...(config.experimental.serverComponentsExternalPackages || []) @@ -1158,261 +1033,12 @@ export default async function getBaseWebpackConfig( .join('|')})[/\\\\]` ) - let resolvedExternalPackageDirs: Map - - async function handleExternals( - context: string, - request: string, - dependencyType: string, - layer: WebpackLayerName | null, - getResolve: ( - options: any - ) => ( - resolveContext: string, - resolveRequest: string - ) => Promise<[string | null, boolean]> - ) { - // We need to externalize internal requests for files intended to - // not be bundled. - const isLocal: boolean = - request.startsWith('.') || - // Always check for unix-style path, as webpack sometimes - // normalizes as posix. - path.posix.isAbsolute(request) || - // When on Windows, we also want to check for Windows-specific - // absolute paths. - (process.platform === 'win32' && path.win32.isAbsolute(request)) - - // make sure import "next" shows a warning when imported - // in pages/components - if (request === 'next') { - return `commonjs next/dist/lib/import-next-warning` - } - - const isAppLayer = isWebpackAppLayer(layer) - - // Relative requires don't need custom resolution, because they - // are relative to requests we've already resolved here. - // Absolute requires (require('/foo')) are extremely uncommon, but - // also have no need for customization as they're already resolved. - if (!isLocal) { - if (/^(?:next$)/.test(request)) { - return `commonjs ${request}` - } - - if (reactPackagesRegex.test(request) && !isAppLayer) { - return `commonjs ${request}` - } - - const notExternalModules = - /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers|router)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ - if (notExternalModules.test(request)) { - return - } - } - - // @swc/helpers should not be external as it would - // require hoisting the package which we can't rely on - if (request.includes('@swc/helpers')) { - return - } - - // BARREL_OPTIMIZATION_PREFIX is a special marker that tells Next.js to - // optimize the import by removing unused exports. This has to be compiled. - if (request.startsWith(BARREL_OPTIMIZATION_PREFIX)) { - return - } - - // When in esm externals mode, and using import, we resolve with - // ESM resolving options. - // Also disable esm request when appDir is enabled - const isEsmRequested = dependencyType === 'esm' - - /** - * @param localRes the full path to the file - * @returns the externalized path - * @description returns an externalized path if the file is a Next.js file and ends with either `.shared-runtime.js` or `.external.js` - * This is used to ensure that files used across the rendering runtime(s) and the user code are one and the same. The logic in this function - * will rewrite the require to the correct bundle location depending on the layer at which the file is being used. - */ - const resolveNextExternal = (localRes: string) => { - const isExternal = externalPattern.test(localRes) - - // if the file ends with .external, we need to make it a commonjs require in all cases - // this is used mainly to share the async local storage across the routing, rendering and user layers. - if (isExternal) { - // it's important we return the path that starts with `next/dist/` here instead of the absolute path - // otherwise NFT will get tripped up - return `commonjs ${localRes.replace(/.*?next[/\\]dist/, 'next/dist')}` - } - } - - // Don't bundle @vercel/og nodejs bundle for nodejs runtime. - // TODO-APP: bundle route.js with different layer that externals common node_module deps. - if ( - isWebpackServerLayer(layer) && - request === 'next/dist/compiled/@vercel/og/index.node.js' - ) { - return `module ${request}` - } - - // Specific Next.js imports that should remain external - // TODO-APP: Investigate if we can remove this. - if (request.startsWith('next/dist/')) { - // Non external that needs to be transpiled - // Image loader needs to be transpiled - if (/^next[\\/]dist[\\/]shared[\\/]lib[\\/]image-loader/.test(request)) { - return - } - - if (/^next[\\/]dist[\\/]compiled[\\/]next-server/.test(request)) { - return `commonjs ${request}` - } - - if ( - /^next[\\/]dist[\\/]shared[\\/](?!lib[\\/]router[\\/]router)/.test( - request - ) || - /^next[\\/]dist[\\/]compiled[\\/].*\.c?js$/.test(request) - ) { - return `commonjs ${request}` - } - - if ( - /^next[\\/]dist[\\/]esm[\\/]shared[\\/](?!lib[\\/]router[\\/]router)/.test( - request - ) || - /^next[\\/]dist[\\/]compiled[\\/].*\.mjs$/.test(request) - ) { - return `module ${request}` - } - - return resolveNextExternal(request) - } - - // Early return if the request needs to be bundled, such as in the client layer. - // Treat react packages and next internals as external for SSR layer, - // also map react to builtin ones with require-hook. - if (layer === WEBPACK_LAYERS.serverSideRendering) { - const isRelative = request.startsWith('.') - const fullRequest = isRelative - ? path.join(context, request).replace(/\\/g, '/') - : request - return resolveNextExternal(fullRequest) - } - - // TODO-APP: Let's avoid this resolve call as much as possible, and eventually get rid of it. - const resolveResult = await resolveExternal( - dir, - config.experimental.esmExternals, - context, - request, - isEsmRequested, - hasAppDir, - getResolve, - isLocal ? resolveNextExternal : undefined - ) - - if ('localRes' in resolveResult) { - return resolveResult.localRes - } - - // Forcedly resolve the styled-jsx installed by next.js, - // since `resolveExternal` cannot find the styled-jsx dep with pnpm - if (request === 'styled-jsx/style') { - resolveResult.res = defaultOverrides['styled-jsx/style'] - } - - const { res, isEsm } = resolveResult - - // If the request cannot be resolved we need to have - // webpack "bundle" it so it surfaces the not found error. - if (!res) { - return - } - - // ESM externals can only be imported (and not required). - // Make an exception in loose mode. - if (!isEsmRequested && isEsm && !looseEsmExternals) { - throw new Error( - `ESM packages (${request}) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals` - ) - } - - const externalType = isEsm ? 'module' : 'commonjs' - - // Default pages have to be transpiled - if ( - // This is the @babel/plugin-transform-runtime "helpers: true" option - /node_modules[/\\]@babel[/\\]runtime[/\\]/.test(res) - ) { - return - } - - // Webpack itself has to be compiled because it doesn't always use module relative paths - if ( - /node_modules[/\\]webpack/.test(res) || - /node_modules[/\\]css-loader/.test(res) - ) { - return - } - - // If a package should be transpiled by Next.js, we skip making it external. - // It doesn't matter what the extension is, as we'll transpile it anyway. - if (config.transpilePackages && !resolvedExternalPackageDirs) { - resolvedExternalPackageDirs = new Map() - // We need to resolve all the external package dirs initially. - for (const pkg of config.transpilePackages) { - const pkgRes = await resolveExternal( - dir, - config.experimental.esmExternals, - context, - pkg + '/package.json', - hasAppDir, - isEsmRequested, - getResolve, - isLocal ? resolveNextExternal : undefined - ) - if (pkgRes.res) { - resolvedExternalPackageDirs.set(pkg, path.dirname(pkgRes.res)) - } - } - } - - // If a package is included in `transpilePackages`, we don't want to make it external. - // And also, if that resource is an ES module, we bundle it too because we can't - // rely on the require hook to alias `react` to our precompiled version. - const shouldBeBundled = - isResourceInPackages( - res, - config.transpilePackages, - resolvedExternalPackageDirs - ) || - (isEsm && isAppLayer) - - if (/node_modules[/\\].*\.[mc]?js$/.test(res)) { - if (isWebpackServerLayer(layer)) { - // All packages should be bundled for the server layer if they're not opted out. - // This option takes priority over the transpilePackages option. - - if (optOutBundlingPackageRegex.test(res)) { - return `${externalType} ${request}` - } - - return - } - - if (shouldBeBundled) return - - // Anything else that is standard JavaScript within `node_modules` - // can be externalized. - return `${externalType} ${request}` - } - - if (shouldBeBundled) return - - // Default behavior: bundle the code! - } + const handleExternals = makeExternalHandler({ + config, + optOutBundlingPackageRegex, + dir, + hasAppDir, + }) const shouldIncludeExternalDirs = config.experimental.externalDir || !!config.transpilePackages diff --git a/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts b/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts index ba7698b5ff489..a4fec7c6ec883 100644 --- a/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts +++ b/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts @@ -14,13 +14,13 @@ import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { NODE_ESM_RESOLVE_OPTIONS, NODE_RESOLVE_OPTIONS, - resolveExternal, } from '../../webpack-config' import { NextConfigComplete } from '../../../server/config-shared' import { loadBindings } from '../../swc' import { isMatch } from 'next/dist/compiled/micromatch' import { getModuleBuildInfo } from '../loaders/get-module-build-info' import { getPageFilePath } from '../../entries' +import { resolveExternal } from '../../handle-externals' const PLUGIN_NAME = 'TraceEntryPointsPlugin' export const TRACE_IGNORES = [ diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index 8e3efecb8b731..0578776fa0ba0 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -30,6 +30,10 @@ const supportedTurbopackNextConfigOptions = [ 'generateEtags', 'assetPrefix', 'distDir', + 'skipMiddlewareUrlNormalize', + 'skipTrailingSlashRedirect', + 'experimental.externalMiddlewareRewritesResolve', + 'experimental.scrollRestoration', 'experimental.serverComponentsExternalPackages', 'experimental.strictNextHead', 'experimental.turbo', diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 3a8f802ea91cd..abbf247206333 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 03d686db47698..d23e8f67a0685 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index dbbd68fb76a42..4ef605b551cc8 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "13.5.4-canary.4", + "version": "13.5.4-canary.5", "private": true, "repository": { "url": "vercel/next.js", @@ -23,7 +23,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "13.5.4-canary.4", + "next": "13.5.4-canary.5", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5baf3f74a11fb..f63c9b6515a0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -726,7 +726,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 @@ -787,7 +787,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../next-env '@swc/helpers': specifier: 0.5.2 @@ -911,19 +911,19 @@ importers: specifier: 1.1.0 version: 1.1.0 '@next/polyfill-module': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../next-polyfill-nomodule '@next/react-dev-overlay': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../react-dev-overlay '@next/react-refresh-utils': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../react-refresh-utils '@next/swc': - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../next-swc '@opentelemetry/api': specifier: 1.4.1 @@ -1584,7 +1584,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 13.5.4-canary.4 + specifier: 13.5.4-canary.5 version: link:../next outdent: specifier: 0.8.0 diff --git a/test/integration/css-customization/test/index.test.js b/test/integration/css-customization/test/index.test.js index 043e0cc744c36..cb4aea1686118 100644 --- a/test/integration/css-customization/test/index.test.js +++ b/test/integration/css-customization/test/index.test.js @@ -8,375 +8,383 @@ import escapeStringRegexp from 'escape-string-regexp' const fixturesDir = join(__dirname, '../..', 'css-fixtures') describe('CSS Customization', () => { - const appDir = join(fixturesDir, 'custom-configuration') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'custom-configuration') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + beforeAll(async () => { + await remove(join(appDir, '.next')) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've compiled and prefixed`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"` - ) - - // Contains a source map - expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) - }) - - it(`should've emitted a source map`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) - - expect(cssMapFiles.length).toBe(1) - const cssMapContent = ( - await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') - ).trim() - - const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) - expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` - Object { - "mappings": "AACA,gCACE,cACE,WACF,CACF,CAGA,OACE,eAA0B,CAA1B,gBACF", - "sourcesContent": Array [ - "/* this should pass through untransformed */ - @media (480px <= width < 768px) { - ::placeholder { - color: green; - } - } - - /* this should be transformed to width/height */ - .video { - -xyz-max-size: 400px 300px; - } - ", - ], - "version": 3, - } - `) - }) -}) -describe('CSS Customization Array', () => { - const appDir = join(fixturesDir, 'custom-configuration-arr') - - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + describe('Basic CSS', () => { + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"` + ) + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + const cssMapContent = ( + await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') + ).trim() + + const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) + expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` +Object { + "mappings": "AACA,gCACE,cACE,WACF,CACF,CAGA,OACE,eAA0B,CAA1B,gBACF", + "sourcesContent": Array [ + "/* this should pass through untransformed */ +@media (480px <= width < 768px) { + ::placeholder { + color: green; + } +} + +/* this should be transformed to width/height */ +.video { + -xyz-max-size: 400px 300px; +} +", + ], + "version": 3, +} +`) + }) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've compiled and prefixed`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `"@media (480px <= width < 768px){a:before{content:\\"\\"}::placeholder{color:green}}.video{max-width:6400px;max-height:4800px;max-width:400rem;max-height:300rem}"` - ) - - // Contains a source map - expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) - }) - - it(`should've emitted a source map`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) - - expect(cssMapFiles.length).toBe(1) - const cssMapContent = ( - await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') - ).trim() - - const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) - expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` - Object { - "mappings": "AACA,gCACE,SACE,UACF,CACA,cACE,WACF,CACF,CAGA,OACE,gBAA4B,CAA5B,iBAA4B,CAA5B,gBAA4B,CAA5B,iBACF", - "sourcesContent": Array [ - "/* this should pass through untransformed */ - @media (480px <= width < 768px) { - a::before { - content: ''; - } - ::placeholder { - color: green; - } - } - - /* this should be transformed to width/height */ - .video { - -xyz-max-size: 400rem 300rem; - } - ", - ], - "version": 3, - } - `) - }) -}) - -describe('CSS Customization custom loader', () => { - const appDir = join(fixturesDir, 'custom-configuration-loader') - - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - - it('should compile successfully', async () => { - const { code, stdout, stderr } = await nextBuild(appDir, [], { - stdout: true, - stderr: true, + describe('Correct CSS Customization Array', () => { + const appDir = join(fixturesDir, 'custom-configuration-arr') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `"@media (480px <= width < 768px){a:before{content:\\"\\"}::placeholder{color:green}}.video{max-width:6400px;max-height:4800px;max-width:400rem;max-height:300rem}"` + ) + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + const cssMapContent = ( + await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') + ).trim() + + const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) + expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` +Object { + "mappings": "AACA,gCACE,SACE,UACF,CACA,cACE,WACF,CACF,CAGA,OACE,gBAA4B,CAA5B,iBAA4B,CAA5B,gBAA4B,CAA5B,iBACF", + "sourcesContent": Array [ + "/* this should pass through untransformed */ +@media (480px <= width < 768px) { + a::before { + content: ''; + } + ::placeholder { + color: green; + } +} + +/* this should be transformed to width/height */ +.video { + -xyz-max-size: 400rem 300rem; +} +", + ], + "version": 3, +} +`) + }) }) - expect(code).toBe(0) - expect(stderr).toMatch(/Built-in CSS support is being disabled/) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've applied style`, async () => { - const pagesFolder = join(appDir, '.next/static/chunks/pages') - - const files = await readdir(pagesFolder) - const indexFiles = files.filter((f) => /^index.+\.js$/.test(f)) - - expect(indexFiles.length).toBe(1) - const indexContent = await readFile( - join(pagesFolder, indexFiles[0]), - 'utf8' - ) - expect(indexContent).toMatch(/\.my-text\.jsx-[0-9a-z]+{color:red}/) - }) -}) - -describe('Bad CSS Customization', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - - it('should compile successfully', async () => { - const { stdout, stderr } = await nextBuild(appDir, [], { - stdout: true, - stderr: true, - }) - expect(stdout).toMatch(/Compiled successfully/) - expect(stderr).toMatch(/field which is not supported.*?sourceMap/) - ;[ - 'postcss-modules-values', - 'postcss-modules-scope', - 'postcss-modules-extract-imports', - 'postcss-modules-local-by-default', - 'postcss-modules', - ].forEach((plugin) => { - expect(stderr).toMatch( - new RegExp(`Please remove the.*?${escapeStringRegexp(plugin)}`) - ) + describe('Correct CSS Customization custom loader', () => { + const appDir = join(fixturesDir, 'custom-configuration-loader') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should compile successfully', async () => { + const { code, stdout, stderr } = await nextBuild(appDir, [], { + stdout: true, + stderr: true, + }) + expect(code).toBe(0) + expect(stderr).toMatch(/Built-in CSS support is being disabled/) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've applied style`, async () => { + const pagesFolder = join(appDir, '.next/static/chunks/pages') + + const files = await readdir(pagesFolder) + const indexFiles = files.filter((f) => /^index.+\.js$/.test(f)) + + expect(indexFiles.length).toBe(1) + const indexContent = await readFile( + join(pagesFolder, indexFiles[0]), + 'utf8' + ) + expect(indexContent).toMatch(/\.my-text\.jsx-[0-9a-z]+{color:red}/) + }) }) - }) - - it(`should've compiled and prefixed`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".video{max-width:400px;max-height:300px}"` - ) - - // Contains a source map - expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) - }) - - it(`should've emitted a source map`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) - - expect(cssMapFiles.length).toBe(1) - }) -}) + describe('Bad CSS Customization', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should compile successfully', async () => { + const { stdout, stderr } = await nextBuild(appDir, [], { + stdout: true, + stderr: true, + }) + expect(stdout).toMatch(/Compiled successfully/) + expect(stderr).toMatch(/field which is not supported.*?sourceMap/) + ;[ + 'postcss-modules-values', + 'postcss-modules-scope', + 'postcss-modules-extract-imports', + 'postcss-modules-local-by-default', + 'postcss-modules', + ].forEach((plugin) => { + expect(stderr).toMatch( + new RegExp(`Please remove the.*?${escapeStringRegexp(plugin)}`) + ) + }) + }) + + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".video{max-width:400px;max-height:300px}"`) + + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) + + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + }) + }) -describe('Bad CSS Customization Array (1)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-1') + describe('Bad CSS Customization Array (1)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-1') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (2)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-2') + describe('Bad CSS Customization Array (2)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-2') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /Error: Your PostCSS configuration for 'postcss-trolling' cannot have null configuration./ - ) - expect(stderr).toMatch( - /To disable 'postcss-trolling', pass false, otherwise, pass true or a configuration object./ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /Error: Your PostCSS configuration for 'postcss-trolling' cannot have null configuration./ + ) + expect(stderr).toMatch( + /To disable 'postcss-trolling', pass false, otherwise, pass true or a configuration object./ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (3)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-3') + describe('Bad CSS Customization Array (3)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-3') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /A PostCSS Plugin must be provided as a string. Instead, we got: '5'/ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /A PostCSS Plugin must be provided as a string. Instead, we got: '5'/ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (4)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-4') + describe('Bad CSS Customization Array (4)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-4') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch(/An unknown PostCSS plugin was provided \(5\)/) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch(/An unknown PostCSS plugin was provided \(5\)/) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (5)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-5') + describe('Bad CSS Customization Array (5)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-5') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /Your custom PostCSS configuration must export a `plugins` key./ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /Your custom PostCSS configuration must export a `plugins` key./ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (6)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-6') + describe('Bad CSS Customization Array (6)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-6') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /Your custom PostCSS configuration must export a `plugins` key./ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /Your custom PostCSS configuration must export a `plugins` key./ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (7)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-7') + describe('Bad CSS Customization Array (7)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-7') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Array (8)', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-arr-8') + describe('Bad CSS Customization Array (8)', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-arr-8') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /A PostCSS Plugin was passed as a function using require\(\), but it must be provided as a string/ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) - }) -}) + expect(stderr).toMatch( + /A PostCSS Plugin was passed as a function using require\(\), but it must be provided as a string/ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) -describe('Bad CSS Customization Function', () => { - const appDir = join(fixturesDir, 'bad-custom-configuration-func') + describe('Bad CSS Customization Function', () => { + const appDir = join(fixturesDir, 'bad-custom-configuration-func') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(stderr).toMatch( - /Your custom PostCSS configuration may not export a function/ - ) - expect(stderr).toMatch(/Build failed because of webpack errors/) + expect(stderr).toMatch( + /Your custom PostCSS configuration may not export a function/ + ) + expect(stderr).toMatch(/Build failed because of webpack errors/) + }) + }) }) }) diff --git a/test/integration/css-modules/test/index.test.js b/test/integration/css-modules/test/index.test.js index 27e565c29b59d..9d45eef3c7d71 100644 --- a/test/integration/css-modules/test/index.test.js +++ b/test/integration/css-modules/test/index.test.js @@ -18,115 +18,121 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../../css-fixtures') describe('Basic CSS Module Support', () => { - const appDir = join(fixturesDir, 'basic-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'basic-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - expect(stdout).toContain('.css') - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + expect(stdout).toContain('.css') + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_redText__honUV{color:red}"` - ) - }) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".index_redText__honUV{color:red}"`) + }) - it(`should've injected the CSS on server render`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) - const cssPreload = $('link[rel="preload"][as="style"]') - expect(cssPreload.length).toBe(1) - expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - const cssSheet = $('link[rel="stylesheet"]') - expect(cssSheet.length).toBe(1) - expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - expect($('#verify-red').attr('class')).toMatchInlineSnapshot( - `"index_redText__honUV"` - ) + expect($('#verify-red').attr('class')).toMatchInlineSnapshot( + `"index_redText__honUV"` + ) + }) }) }) describe('3rd Party CSS Module Support', () => { - const appDir = join(fixturesDir, '3rd-party-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, '3rd-party-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_foo__6TgnK{position:relative}.index_foo__6TgnK .bar,.index_foo__6TgnK .baz{height:100%;overflow:hidden}.index_foo__6TgnK .lol,.index_foo__6TgnK>.lel{width:80%}"` - ) - }) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".index_foo__6TgnK{position:relative}.index_foo__6TgnK .bar,.index_foo__6TgnK .baz{height:100%;overflow:hidden}.index_foo__6TgnK .lol,.index_foo__6TgnK>.lel{width:80%}"` + ) + }) - it(`should've injected the CSS on server render`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) - const cssPreload = $('link[rel="preload"][as="style"]') - expect(cssPreload.length).toBe(1) - expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - const cssSheet = $('link[rel="stylesheet"]') - expect(cssSheet.length).toBe(1) - expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - expect($('#verify-div').attr('class')).toMatchInlineSnapshot( - `"index_foo__6TgnK"` - ) + expect($('#verify-div').attr('class')).toMatchInlineSnapshot( + `"index_foo__6TgnK"` + ) + }) }) }) @@ -155,36 +161,38 @@ describe('Has CSS Module in computed styles in Development', () => { }) describe('Has CSS Module in computed styles in Production', () => { - const appDir = join(fixturesDir, 'prod-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'prod-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it('should have CSS for page', async () => { - const browser = await webdriver(appPort, '/') + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/') - const currentColor = await browser.eval( - `window.getComputedStyle(document.querySelector('#verify-red')).color` - ) - expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + }) }) }) @@ -277,271 +285,291 @@ describe.skip('Invalid Global CSS Module Usage in node_modules', () => { }) describe('Valid CSS Module Usage from within node_modules', () => { - const appDir = join(fixturesDir, 'nm-module') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'nm-module') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it(`should've prerendered with relevant data`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) + it(`should've prerendered with relevant data`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) - const cssPreload = $('#nm-div') - expect(cssPreload.text()).toMatchInlineSnapshot( - `"{\\"message\\":\\"Why hello there\\"} {\\"redText\\":\\"example_redText__0ctGB\\"}"` - ) - }) + const cssPreload = $('#nm-div') + expect(cssPreload.text()).toMatchInlineSnapshot( + `"{\\"message\\":\\"Why hello there\\"} {\\"redText\\":\\"example_redText__0ctGB\\"}"` + ) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".example_redText__0ctGB{color:red}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".example_redText__0ctGB{color:red}"`) + }) }) }) describe('Valid Nested CSS Module Usage from within node_modules', () => { - const appDir = join(fixturesDir, 'nm-module-nested') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'nm-module-nested') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it(`should've prerendered with relevant data`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) + it(`should've prerendered with relevant data`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) - const cssPreload = $('#nm-div') - expect(cssPreload.text()).toMatchInlineSnapshot( - `"{\\"message\\":\\"Why hello there\\"} {\\"subClass\\":\\"example_subClass__m6Tyy other_className__OA8dV\\"}"` - ) - }) + const cssPreload = $('#nm-div') + expect(cssPreload.text()).toMatchInlineSnapshot( + `"{\\"message\\":\\"Why hello there\\"} {\\"subClass\\":\\"example_subClass__m6Tyy other_className__OA8dV\\"}"` + ) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".other2_other2__dYPgz{color:red}.other3_other3__7hgUE{color:violet}.other_className__OA8dV{background:red;color:#ff0}.example_subClass__m6Tyy{background:blue}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".other2_other2__dYPgz{color:red}.other3_other3__7hgUE{color:violet}.other_className__OA8dV{background:red;color:#ff0}.example_subClass__m6Tyy{background:blue}"` + ) + }) }) }) describe('CSS Module Composes Usage (Basic)', () => { - // This is a very bad feature. Do not use it. - const appDir = join(fixturesDir, 'composes-basic') - - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-basic') + + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_className__jjcZ1{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".index_className__jjcZ1{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"` + ) + }) }) }) describe('CSS Module Composes Usage (External)', () => { - // This is a very bad feature. Do not use it. - const appDir = join(fixturesDir, 'composes-external') - - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-external') + + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".other_className__eZV4M{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".other_className__eZV4M{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"` + ) + }) }) }) describe('Dynamic Route CSS Module Usage', () => { - const appDir = join(fixturesDir, 'dynamic-route-module') - - let stdout - let code - let app - let appPort - - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(() => killApp(app)) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'dynamic-route-module') + + let stdout + let code + let app + let appPort + + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it('should apply styles correctly', async () => { - const browser = await webdriver(appPort, '/post-1') + it('should apply styles correctly', async () => { + const browser = await webdriver(appPort, '/post-1') - const background = await browser - .elementByCss('#my-div') - .getComputedCss('background-color') + const background = await browser + .elementByCss('#my-div') + .getComputedCss('background-color') - expect(background).toMatch(/rgb(a|)\(255, 0, 0/) - }) + expect(background).toMatch(/rgb(a|)\(255, 0, 0/) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `"._post__home__yRmHz{background:red}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`"._post__home__yRmHz{background:red}"`) + }) }) }) describe('Catch-all Route CSS Module Usage', () => { - const appDir = join(fixturesDir, 'catch-all-module') - - let stdout - let code - let app - let appPort - - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(() => killApp(app)) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'catch-all-module') + + let stdout + let code + let app + let appPort + + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it('should apply styles correctly', async () => { - const browser = await webdriver(appPort, '/post-1') + it('should apply styles correctly', async () => { + const browser = await webdriver(appPort, '/post-1') - const bg = await browser - .elementByCss('#my-div') - .getComputedCss('background-color') - expect(bg).toMatch(/rgb(a|)\(255, 0, 0/) + const bg = await browser + .elementByCss('#my-div') + .getComputedCss('background-color') + expect(bg).toMatch(/rgb(a|)\(255, 0, 0/) - const fg = await browser.elementByCss('#my-div').getComputedCss('color') - expect(fg).toMatch(/rgb(a|)\(0, 128, 0/) - }) + const fg = await browser.elementByCss('#my-div').getComputedCss('color') + expect(fg).toMatch(/rgb(a|)\(0, 128, 0/) + }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".___post__home__e4zfx{background:red}.__55css_home__r8Rnq{color:green}"` - ) + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".___post__home__e4zfx{background:red}.__55css_home__r8Rnq{color:green}"` + ) + }) }) }) diff --git a/test/integration/css/test/basic-global-support.test.js b/test/integration/css/test/basic-global-support.test.js index e956947422219..a2f9dee9d5380 100644 --- a/test/integration/css/test/basic-global-support.test.js +++ b/test/integration/css/test/basic-global-support.test.js @@ -7,311 +7,331 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../..', 'css-fixtures') describe('Basic Global Support', () => { - const appDir = join(fixturesDir, 'single-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'single-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Basic Global Support with special characters in path', () => { - const appDir = join(fixturesDir, 'single-global-special-characters', 'a+b') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'single-global-special-characters', 'a+b') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Basic Global Support with src/ dir', () => { - const appDir = join(fixturesDir, 'single-global-src') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'single-global-src') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Multi Global Support', () => { - const appDir = join(fixturesDir, 'multi-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'multi-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".red-text{color:red}.blue-text{color:blue}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".red-text{color:red}.blue-text{color:blue}"`) + }) }) }) describe('Nested @import() Global Support', () => { - const appDir = join(fixturesDir, 'nested-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'nested-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".red-text{color:purple;font-weight:bolder;color:red}.blue-text{color:orange;font-weight:bolder;color:blue}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".red-text{color:purple;font-weight:bolder;color:red}.blue-text{color:orange;font-weight:bolder;color:blue}"` + ) + }) }) }) // Tests css ordering describe('Multi Global Support (reversed)', () => { - const appDir = join(fixturesDir, 'multi-global-reversed') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'multi-global-reversed') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".blue-text{color:blue}.red-text{color:red}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".blue-text{color:blue}.red-text{color:red}"`) + }) }) }) describe('CSS URL via `file-loader`', () => { - const appDir = join(fixturesDir, 'url-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'url-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` Array [ "dark.svg", "dark2.svg", "light.svg", ] `) + }) }) }) describe('CSS URL via `file-loader` and asset prefix (1)', () => { - const appDir = join(fixturesDir, 'url-global-asset-prefix-1') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-1') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` Array [ "dark.svg", "dark2.svg", "light.svg", ] `) + }) }) }) describe('CSS URL via `file-loader` and asset prefix (2)', () => { - const appDir = join(fixturesDir, 'url-global-asset-prefix-2') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-2') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` Array [ "dark.svg", "dark2.svg", "light.svg", ] `) + }) }) }) diff --git a/test/integration/css/test/css-modules.test.js b/test/integration/css/test/css-modules.test.js index d2eb4591b5b66..277ee19987703 100644 --- a/test/integration/css/test/css-modules.test.js +++ b/test/integration/css/test/css-modules.test.js @@ -111,57 +111,61 @@ describe('Basic CSS Modules Ordering', () => { }) describe('should handle unresolved files gracefully', () => { - const workDir = join(fixturesDir, 'unresolved-css-url') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const workDir = join(fixturesDir, 'unresolved-css-url') - it('should build correctly', async () => { - await remove(join(workDir, '.next')) - const { code } = await nextBuild(workDir) - expect(code).toBe(0) - }) + it('should build correctly', async () => { + await remove(join(workDir, '.next')) + const { code } = await nextBuild(workDir) + expect(code).toBe(0) + }) - it('should have correct file references in CSS output', async () => { - const cssFiles = await readdir(join(workDir, '.next/static/css')) + it('should have correct file references in CSS output', async () => { + const cssFiles = await readdir(join(workDir, '.next/static/css')) - for (const file of cssFiles) { - if (file.endsWith('.css.map')) continue + for (const file of cssFiles) { + if (file.endsWith('.css.map')) continue - const content = await readFile( - join(workDir, '.next/static/css', file), - 'utf8' - ) - console.log(file, content) + const content = await readFile( + join(workDir, '.next/static/css', file), + 'utf8' + ) + console.log(file, content) - // if it is the combined global CSS file there are double the expected - // results - const howMany = content.includes('p{') || content.includes('p,') ? 2 : 1 + // if it is the combined global CSS file there are double the expected + // results + const howMany = content.includes('p{') || content.includes('p,') ? 2 : 1 - expect(content.match(/\(\/vercel\.svg/g).length).toBe(howMany) - // expect(content.match(/\(vercel\.svg/g).length).toBe(howMany) - expect(content.match(/\(\/_next\/static\/media/g).length).toBe(1) - expect(content.match(/\(https:\/\//g).length).toBe(howMany) - } + expect(content.match(/\(\/vercel\.svg/g).length).toBe(howMany) + // expect(content.match(/\(vercel\.svg/g).length).toBe(howMany) + expect(content.match(/\(\/_next\/static\/media/g).length).toBe(1) + expect(content.match(/\(https:\/\//g).length).toBe(howMany) + } + }) }) }) describe('Data URLs', () => { - const workDir = join(fixturesDir, 'data-url') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const workDir = join(fixturesDir, 'data-url') - it('should compile successfully', async () => { - await remove(join(workDir, '.next')) - const { code } = await nextBuild(workDir) - expect(code).toBe(0) - }) + it('should compile successfully', async () => { + await remove(join(workDir, '.next')) + const { code } = await nextBuild(workDir) + expect(code).toBe(0) + }) - it('should have emitted expected files', async () => { - const cssFolder = join(workDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + it('should have emitted expected files', async () => { + const cssFolder = join(workDir, '.next/static/css') + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /background:url\("data:[^"]+"\)/ - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /background:url\("data:[^"]+"\)/ + ) + }) }) }) @@ -249,36 +253,38 @@ describe('Ordering with Global CSS and Modules (dev)', () => { }) describe('Ordering with Global CSS and Modules (prod)', () => { - const appDir = join(fixturesDir, 'global-and-module-ordering') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'global-and-module-ordering') - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - it('should have the correct color (css ordering)', async () => { - const browser = await webdriver(appPort, '/') + it('should have the correct color (css ordering)', async () => { + const browser = await webdriver(appPort, '/') - const currentColor = await browser.eval( - `window.getComputedStyle(document.querySelector('#blueText')).color` - ) - expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#blueText')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + }) }) }) diff --git a/test/integration/font-optimization/test/index.test.js b/test/integration/font-optimization/test/index.test.js index b0c7d804e8ff8..993d514d830a6 100644 --- a/test/integration/font-optimization/test/index.test.js +++ b/test/integration/font-optimization/test/index.test.js @@ -178,150 +178,186 @@ describe('Font Optimization', () => { }) // Re-run build to check if it works when build is cached - it('should work when build is cached', async () => { - await nextBuild(appDir) - const testJson = JSON.parse( - await fs.readFile(builtPage('font-manifest.json'), { - encoding: 'utf-8', - }) - ) - expect(testJson.length).toBeGreaterThan(0) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should work when build is cached', + async () => { + await nextBuild(appDir) + const testJson = JSON.parse( + await fs.readFile(builtPage('font-manifest.json'), { + encoding: 'utf-8', + }) + ) + expect(testJson.length).toBeGreaterThan(0) + } + ) } describe('Font optimization for SSR apps', () => { - beforeAll(async () => { - if (fs.pathExistsSync(join(appDir, '.next'))) { - await fs.remove(join(appDir, '.next')) + ;(process.env.TURBOPACK ? describe.skip : describe)( + 'production mode', + () => { + beforeAll(async () => { + if (fs.pathExistsSync(join(appDir, '.next'))) { + await fs.remove(join(appDir, '.next')) + } + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + builtServerPagesDir = join(appDir, '.next', 'server') + builtPage = (file) => join(builtServerPagesDir, file) + }) + afterAll(() => killApp(app)) + runTests() } - await nextBuild(appDir) - appPort = await findPort() - app = await nextStart(appDir, appPort) - builtServerPagesDir = join(appDir, '.next', 'server') - builtPage = (file) => join(builtServerPagesDir, file) - }) - afterAll(() => killApp(app)) - runTests() + ) }) describe('Font optimization for unreachable font definitions.', () => { + ;(process.env.TURBOPACK ? describe.skip : describe)( + 'production mode', + () => { + beforeAll(async () => { + await nextBuild(appDir) + await fs.writeFile( + join(appDir, '.next', 'server', 'font-manifest.json'), + '[]', + 'utf8' + ) + appPort = await findPort() + app = await nextStart(appDir, appPort) + builtServerPagesDir = join(appDir, '.next', 'server') + builtPage = (file) => join(builtServerPagesDir, file) + }) + afterAll(() => killApp(app)) + it('should fallback to normal stylesheet if the contents of the fonts are unreachable', async () => { + const html = await renderViaHTTP(appPort, '/stars') + expect(await fsExists(builtPage('font-manifest.json'))).toBe(true) + expect(html).toContain( + `` + ) + }) + it('should not inline multiple fallback link tag', async () => { + await renderViaHTTP(appPort, '/stars') + // second render to make sure that the page is requested more than once. + const html = await renderViaHTTP(appPort, '/stars') + expect(await fsExists(builtPage('font-manifest.json'))).toBe(true) + expect(html).not.toContain( + `` + ) + }) + } + ) + }) + } + ) + ;(process.env.TURBOPACK ? it.skip : it)( + 'Spread operator regression on ', + async () => { + const appDir = join(fixturesDir, 'spread-operator-regression') + const { code } = await nextBuild(appDir) + // eslint-disable-next-line + expect(code).toBe(0) + } + ) + ;(process.env.TURBOPACK ? it.skip : it)( + 'makeStylesheetInert regression', + async () => { + const appDir = join(fixturesDir, 'make-stylesheet-inert-regression') + const { code } = await nextBuild(appDir) + // eslint-disable-next-line + expect(code).toBe(0) + } + ) + + describe('font override', () => { + ;(process.env.TURBOPACK ? describe.skip : describe)( + 'production mode', + () => { + let app, appPort + beforeAll(async () => { + const appDir = join(fixturesDir, 'font-override') await nextBuild(appDir) - await fs.writeFile( - join(appDir, '.next', 'server', 'font-manifest.json'), - '[]', - 'utf8' - ) appPort = await findPort() app = await nextStart(appDir, appPort) - builtServerPagesDir = join(appDir, '.next', 'server') - builtPage = (file) => join(builtServerPagesDir, file) }) afterAll(() => killApp(app)) - it('should fallback to normal stylesheet if the contents of the fonts are unreachable', async () => { - const html = await renderViaHTTP(appPort, '/stars') - expect(await fsExists(builtPage('font-manifest.json'))).toBe(true) - expect(html).toContain(``) - }) - it('should not inline multiple fallback link tag', async () => { - await renderViaHTTP(appPort, '/stars') - // second render to make sure that the page is requested more than once. - const html = await renderViaHTTP(appPort, '/stars') - expect(await fsExists(builtPage('font-manifest.json'))).toBe(true) - expect(html).not.toContain( - `` + it('should inline font-override values', async () => { + const html = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(html) + const inlineStyle = $( + 'style[data-href="https://fonts.googleapis.com/css2?family=Roboto&display=swap"]' + ) + const inlineStyleMultiple = $( + 'style[data-href="https://fonts.googleapis.com/css2?family=Open+Sans&family=Libre+Baskerville&display=swap"]' + ) + expect(inlineStyle.length).toBe(1) + expect(inlineStyle.html()).toContain( + '@font-face{font-family:"Roboto Fallback";ascent-override:92.77%;descent-override:24.41%;line-gap-override:0.00%;src:local("Arial")}' + ) + expect(inlineStyleMultiple.length).toBe(1) + expect(inlineStyleMultiple.html()).toContain( + '@font-face{font-family:"Libre Baskerville Fallback";ascent-override:97.00%;descent-override:27.00%;line-gap-override:0.00%;src:local("Times New Roman")}' + ) + expect(inlineStyleMultiple.html()).toContain( + '@font-face{font-family:"Open Sans Fallback";ascent-override:106.88%;descent-override:29.30%;line-gap-override:0.00%;src:local("Arial")}' ) }) - }) - } - ) - - test('Spread operator regression on ', async () => { - const appDir = join(fixturesDir, 'spread-operator-regression') - const { code } = await nextBuild(appDir) - expect(code).toBe(0) - }) - - test('makeStylesheetInert regression', async () => { - const appDir = join(fixturesDir, 'make-stylesheet-inert-regression') - const { code } = await nextBuild(appDir) - expect(code).toBe(0) - }) - - describe('font override', () => { - let app, appPort - - beforeAll(async () => { - const appDir = join(fixturesDir, 'font-override') - await nextBuild(appDir) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(() => killApp(app)) - it('should inline font-override values', async () => { - const html = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(html) - const inlineStyle = $( - 'style[data-href="https://fonts.googleapis.com/css2?family=Roboto&display=swap"]' - ) - const inlineStyleMultiple = $( - 'style[data-href="https://fonts.googleapis.com/css2?family=Open+Sans&family=Libre+Baskerville&display=swap"]' - ) - expect(inlineStyle.length).toBe(1) - expect(inlineStyle.html()).toContain( - '@font-face{font-family:"Roboto Fallback";ascent-override:92.77%;descent-override:24.41%;line-gap-override:0.00%;src:local("Arial")}' - ) - expect(inlineStyleMultiple.length).toBe(1) - expect(inlineStyleMultiple.html()).toContain( - '@font-face{font-family:"Libre Baskerville Fallback";ascent-override:97.00%;descent-override:27.00%;line-gap-override:0.00%;src:local("Times New Roman")}' - ) - expect(inlineStyleMultiple.html()).toContain( - '@font-face{font-family:"Open Sans Fallback";ascent-override:106.88%;descent-override:29.30%;line-gap-override:0.00%;src:local("Arial")}' - ) - }) + } + ) }) describe('font override with size adjust', () => { - let app, appPort + ;(process.env.TURBOPACK ? describe.skip : describe)( + 'production mode', + () => { + let app, appPort - beforeAll(async () => { - const appDir = join(fixturesDir, 'font-override-size-adjust') - await nextBuild(appDir) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(() => killApp(app)) - it('should inline font-override values', async () => { - const html = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(html) - const inlineStyle = $( - 'style[data-href="https://fonts.googleapis.com/css2?family=Roboto&display=swap"]' - ) - const inlineStyleMultiple = $( - 'style[data-href="https://fonts.googleapis.com/css2?family=Open+Sans&family=Libre+Baskerville&display=swap"]' - ) - expect(inlineStyle.length).toBe(1) - expect(inlineStyle.html()).toContain( - '@font-face{font-family:"Roboto Fallback";ascent-override:92.67%;descent-override:24.39%;line-gap-override:0.00%;size-adjust:100.11%;src:local("Arial")}' - ) - expect(inlineStyleMultiple.length).toBe(1) - expect(inlineStyleMultiple.html()).toContain( - '@font-face{font-family:"Libre Baskerville Fallback";ascent-override:75.76%;descent-override:21.09%;line-gap-override:0.00%;size-adjust:128.03%;src:local("Times New Roman")}' - ) - expect(inlineStyleMultiple.html()).toContain( - '@font-face{font-family:"Open Sans Fallback";ascent-override:101.18%;descent-override:27.73%;line-gap-override:0.00%;size-adjust:105.64%;src:local("Arial")}' - ) - }) + beforeAll(async () => { + const appDir = join(fixturesDir, 'font-override-size-adjust') + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + it('should inline font-override values', async () => { + const html = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(html) + const inlineStyle = $( + 'style[data-href="https://fonts.googleapis.com/css2?family=Roboto&display=swap"]' + ) + const inlineStyleMultiple = $( + 'style[data-href="https://fonts.googleapis.com/css2?family=Open+Sans&family=Libre+Baskerville&display=swap"]' + ) + expect(inlineStyle.length).toBe(1) + expect(inlineStyle.html()).toContain( + '@font-face{font-family:"Roboto Fallback";ascent-override:92.67%;descent-override:24.39%;line-gap-override:0.00%;size-adjust:100.11%;src:local("Arial")}' + ) + expect(inlineStyleMultiple.length).toBe(1) + expect(inlineStyleMultiple.html()).toContain( + '@font-face{font-family:"Libre Baskerville Fallback";ascent-override:75.76%;descent-override:21.09%;line-gap-override:0.00%;size-adjust:128.03%;src:local("Times New Roman")}' + ) + expect(inlineStyleMultiple.html()).toContain( + '@font-face{font-family:"Open Sans Fallback";ascent-override:101.18%;descent-override:27.73%;line-gap-override:0.00%;size-adjust:105.64%;src:local("Arial")}' + ) + }) + } + ) }) describe('invalid configuration', () => { - it('should show a proper error if assetPrefix starts with .', async () => { - const appDir = join(fixturesDir, 'invalid-assertprefix') - const { stderr } = await nextBuild(appDir, undefined, { - stderr: true, - }) - expect(stderr).toContain( - 'assetPrefix must start with a leading slash or be an absolute URL(http:// or https://)' - ) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)( + 'production mode', + () => { + it('should show a proper error if assetPrefix starts with .', async () => { + const appDir = join(fixturesDir, 'invalid-assertprefix') + const { stderr } = await nextBuild(appDir, undefined, { + stderr: true, + }) + expect(stderr).toContain( + 'assetPrefix must start with a leading slash or be an absolute URL(http:// or https://)' + ) + }) + } + ) }) }) diff --git a/test/integration/next-image-new/loader-config/test/index.test.ts b/test/integration/next-image-new/loader-config/test/index.test.ts index 010944a321289..842b627d24851 100644 --- a/test/integration/next-image-new/loader-config/test/index.test.ts +++ b/test/integration/next-image-new/loader-config/test/index.test.ts @@ -37,7 +37,7 @@ function runTests(url: string) { }) } -describe('Image Loader Config', () => { +describe('Image Loader Config new', () => { describe('dev mode - component', () => { beforeAll(async () => { appPort = await findPort() diff --git a/test/integration/project-dir-delete/index.test.ts b/test/integration/project-dir-delete/index.test.ts deleted file mode 100644 index 54d18aa652147..0000000000000 --- a/test/integration/project-dir-delete/index.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - check, - findPort, - killApp, - launchApp, - renderViaHTTP, -} from 'next-test-utils' -import { join } from 'path' -import fs from 'fs-extra' -import stripAnsi from 'strip-ansi' - -describe.skip('Project Directory Delete Handling', () => { - it('should gracefully exit on project dir delete', async () => { - const appDir = join(__dirname, 'app') - const appPort = await findPort() - - await fs.ensureDir(join(appDir, 'pages')) - await fs.writeFile( - join(appDir, 'pages', 'index.js'), - ` - export default function Page() { - return

hello world

- } - ` - ) - let output = '' - - const app = await launchApp(appDir, appPort, { - onStdout(msg) { - output += msg - }, - onStderr(msg) { - output += msg - }, - }) - - expect(await renderViaHTTP(appPort, '/')).toContain('hello world') - await fs.remove(appDir) - - await check( - () => stripAnsi(output), - /Project directory could not be found, restart Next\.js in your new directory/ - ) - - try { - await killApp(app) - } catch (_) {} - }) -}) diff --git a/test/integration/query-with-encoding/test/index.test.js b/test/integration/query-with-encoding/test/index.test.js index e7575f35d15a0..6ce0457d5e842 100644 --- a/test/integration/query-with-encoding/test/index.test.js +++ b/test/integration/query-with-encoding/test/index.test.js @@ -11,208 +11,234 @@ let app let server describe('Query String with Encoding', () => { - beforeAll(async () => { - await nextBuild(appDir) - app = nextServer({ - dir: join(__dirname, '../'), - dev: false, - quiet: true, - }) - - server = await startApp(app) - appPort = server.address().port - }) - afterAll(() => stopApp(server)) - - describe('new line', () => { - it('should have correct query on SSR', async () => { - const browser = await webdriver(appPort, '/?test=abc%0A') - try { - const text = await browser.elementByCss('#query-content').text() - expect(text).toBe('{"test":"abc\\n"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on Router#push', async () => { - const browser = await webdriver(appPort, '/') - try { - await browser.waitForCondition('!!window.next.router') - await browser.eval( - `window.next.router.push({pathname:'/',query:{abc:'def\\n'}})` - ) - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"abc":"def\\n"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on simple client-side ', async () => { - const browser = await webdriver(appPort, '/newline') - try { - await browser.waitForElementByCss('#hello-lf').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"another":"hello\\n"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on complex client-side ', async () => { - const browser = await webdriver(appPort, '/newline') - try { - await browser.waitForElementByCss('#hello-complex').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"complex":"yes\\n"}') - } finally { - await browser.close() - } - }) - }) - - describe('trailing space', () => { - it('should have correct query on SSR', async () => { - const browser = await webdriver(appPort, '/?test=abc%20') - try { - const text = await browser.elementByCss('#query-content').text() - expect(text).toBe('{"test":"abc "}') - } finally { - await browser.close() - } - }) - - it('should have correct query on Router#push', async () => { - const browser = await webdriver(appPort, '/') - try { - await browser.waitForCondition('!!window.next.router') - await browser.eval( - `window.next.router.push({pathname:'/',query:{abc:'def '}})` - ) - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"abc":"def "}') - } finally { - await browser.close() - } - }) - - it('should have correct query on simple client-side ', async () => { - const browser = await webdriver(appPort, '/space') - try { - await browser.waitForElementByCss('#hello-space').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"another":"hello "}') - } finally { - await browser.close() - } - }) - - it('should have correct query on complex client-side ', async () => { - const browser = await webdriver(appPort, '/space') - try { - await browser.waitForElementByCss('#hello-complex').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"complex":"yes "}') - } finally { - await browser.close() - } - }) - }) - - describe('percent', () => { - it('should have correct query on SSR', async () => { - const browser = await webdriver(appPort, '/?test=abc%25') - try { - const text = await browser.elementByCss('#query-content').text() - expect(text).toBe('{"test":"abc%"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on Router#push', async () => { - const browser = await webdriver(appPort, '/') - try { - await browser.waitForCondition('!!window.next.router') - await browser.eval( - `window.next.router.push({pathname:'/',query:{abc:'def%'}})` - ) - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"abc":"def%"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on simple client-side ', async () => { - const browser = await webdriver(appPort, '/percent') - try { - await browser.waitForElementByCss('#hello-percent').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"another":"hello%"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on complex client-side ', async () => { - const browser = await webdriver(appPort, '/percent') - try { - await browser.waitForElementByCss('#hello-complex').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"complex":"yes%"}') - } finally { - await browser.close() - } - }) - }) - - describe('plus', () => { - it('should have correct query on SSR', async () => { - const browser = await webdriver(appPort, '/?test=abc%2B') - try { - const text = await browser.elementByCss('#query-content').text() - expect(text).toBe('{"test":"abc+"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on Router#push', async () => { - const browser = await webdriver(appPort, '/') - try { - await browser.waitForCondition('!!window.next.router') - await browser.eval( - `window.next.router.push({pathname:'/',query:{abc:'def+'}})` - ) - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"abc":"def+"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on simple client-side ', async () => { - const browser = await webdriver(appPort, '/plus') - try { - await browser.waitForElementByCss('#hello-plus').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"another":"hello+"}') - } finally { - await browser.close() - } - }) - - it('should have correct query on complex client-side ', async () => { - const browser = await webdriver(appPort, '/plus') - try { - await browser.waitForElementByCss('#hello-complex').click() - const text = await browser.waitForElementByCss('#query-content').text() - expect(text).toBe('{"complex":"yes+"}') - } finally { - await browser.close() - } + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + app = nextServer({ + dir: join(__dirname, '../'), + dev: false, + quiet: true, + }) + + server = await startApp(app) + appPort = server.address().port + }) + afterAll(() => stopApp(server)) + + describe('new line', () => { + it('should have correct query on SSR', async () => { + const browser = await webdriver(appPort, '/?test=abc%0A') + try { + const text = await browser.elementByCss('#query-content').text() + expect(text).toBe('{"test":"abc\\n"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on Router#push', async () => { + const browser = await webdriver(appPort, '/') + try { + await browser.waitForCondition('!!window.next.router') + await browser.eval( + `window.next.router.push({pathname:'/',query:{abc:'def\\n'}})` + ) + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"abc":"def\\n"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on simple client-side ', async () => { + const browser = await webdriver(appPort, '/newline') + try { + await browser.waitForElementByCss('#hello-lf').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"another":"hello\\n"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on complex client-side ', async () => { + const browser = await webdriver(appPort, '/newline') + try { + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"complex":"yes\\n"}') + } finally { + await browser.close() + } + }) + }) + + describe('trailing space', () => { + it('should have correct query on SSR', async () => { + const browser = await webdriver(appPort, '/?test=abc%20') + try { + const text = await browser.elementByCss('#query-content').text() + expect(text).toBe('{"test":"abc "}') + } finally { + await browser.close() + } + }) + + it('should have correct query on Router#push', async () => { + const browser = await webdriver(appPort, '/') + try { + await browser.waitForCondition('!!window.next.router') + await browser.eval( + `window.next.router.push({pathname:'/',query:{abc:'def '}})` + ) + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"abc":"def "}') + } finally { + await browser.close() + } + }) + + it('should have correct query on simple client-side ', async () => { + const browser = await webdriver(appPort, '/space') + try { + await browser.waitForElementByCss('#hello-space').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"another":"hello "}') + } finally { + await browser.close() + } + }) + + it('should have correct query on complex client-side ', async () => { + const browser = await webdriver(appPort, '/space') + try { + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"complex":"yes "}') + } finally { + await browser.close() + } + }) + }) + + describe('percent', () => { + it('should have correct query on SSR', async () => { + const browser = await webdriver(appPort, '/?test=abc%25') + try { + const text = await browser.elementByCss('#query-content').text() + expect(text).toBe('{"test":"abc%"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on Router#push', async () => { + const browser = await webdriver(appPort, '/') + try { + await browser.waitForCondition('!!window.next.router') + await browser.eval( + `window.next.router.push({pathname:'/',query:{abc:'def%'}})` + ) + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"abc":"def%"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on simple client-side ', async () => { + const browser = await webdriver(appPort, '/percent') + try { + await browser.waitForElementByCss('#hello-percent').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"another":"hello%"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on complex client-side ', async () => { + const browser = await webdriver(appPort, '/percent') + try { + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"complex":"yes%"}') + } finally { + await browser.close() + } + }) + }) + + describe('plus', () => { + it('should have correct query on SSR', async () => { + const browser = await webdriver(appPort, '/?test=abc%2B') + try { + const text = await browser.elementByCss('#query-content').text() + expect(text).toBe('{"test":"abc+"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on Router#push', async () => { + const browser = await webdriver(appPort, '/') + try { + await browser.waitForCondition('!!window.next.router') + await browser.eval( + `window.next.router.push({pathname:'/',query:{abc:'def+'}})` + ) + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"abc":"def+"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on simple client-side ', async () => { + const browser = await webdriver(appPort, '/plus') + try { + await browser.waitForElementByCss('#hello-plus').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"another":"hello+"}') + } finally { + await browser.close() + } + }) + + it('should have correct query on complex client-side ', async () => { + const browser = await webdriver(appPort, '/plus') + try { + await browser.waitForElementByCss('#hello-complex').click() + const text = await browser + .waitForElementByCss('#query-content') + .text() + expect(text).toBe('{"complex":"yes+"}') + } finally { + await browser.close() + } + }) }) }) }) diff --git a/test/integration/scss-modules/test/basic-scss.test.js b/test/integration/scss-modules/test/basic-scss.test.js index 48ff2783f7dae..a6ceb9377a116 100644 --- a/test/integration/scss-modules/test/basic-scss.test.js +++ b/test/integration/scss-modules/test/basic-scss.test.js @@ -18,114 +18,120 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../../scss-fixtures') describe('Basic SCSS Module Support', () => { - const appDir = join(fixturesDir, 'basic-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) - - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_redText__zXafh{color:red}"` - ) - }) - - it(`should've injected the CSS on server render`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) - - const cssPreload = $('link[rel="preload"][as="style"]') - expect(cssPreload.length).toBe(1) - expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - - const cssSheet = $('link[rel="stylesheet"]') - expect(cssSheet.length).toBe(1) - expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - - expect($('#verify-red').attr('class')).toMatchInlineSnapshot( - `"index_redText__zXafh"` - ) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'basic-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".index_redText__zXafh{color:red}"`) + }) + + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) + + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + + expect($('#verify-red').attr('class')).toMatchInlineSnapshot( + `"index_redText__zXafh"` + ) + }) }) }) describe('3rd Party CSS Module Support', () => { - const appDir = join(fixturesDir, '3rd-party-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) - - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_foo__72b4D{position:relative}.index_foo__72b4D .bar,.index_foo__72b4D .baz{height:100%;overflow:hidden}.index_foo__72b4D .lol,.index_foo__72b4D>.lel{width:80%}"` - ) - }) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, '3rd-party-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".index_foo__72b4D{position:relative}.index_foo__72b4D .bar,.index_foo__72b4D .baz{height:100%;overflow:hidden}.index_foo__72b4D .lol,.index_foo__72b4D>.lel{width:80%}"` + ) + }) - it(`should've injected the CSS on server render`, async () => { - const content = await renderViaHTTP(appPort, '/') - const $ = cheerio.load(content) + it(`should've injected the CSS on server render`, async () => { + const content = await renderViaHTTP(appPort, '/') + const $ = cheerio.load(content) - const cssPreload = $('link[rel="preload"][as="style"]') - expect(cssPreload.length).toBe(1) - expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - const cssSheet = $('link[rel="stylesheet"]') - expect(cssSheet.length).toBe(1) - expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - expect($('#verify-div').attr('class')).toMatchInlineSnapshot( - `"index_foo__72b4D"` - ) + expect($('#verify-div').attr('class')).toMatchInlineSnapshot( + `"index_foo__72b4D"` + ) + }) }) }) @@ -154,36 +160,38 @@ describe('Has CSS Module in computed styles in Development', () => { }) describe('Has CSS Module in computed styles in Production', () => { - const appDir = join(fixturesDir, 'prod-module') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) - - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it('should have CSS for page', async () => { - const browser = await webdriver(appPort, '/') - - const currentColor = await browser.eval( - `window.getComputedStyle(document.querySelector('#verify-red')).color` - ) - expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'prod-module') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/') + + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + }) }) }) @@ -234,67 +242,75 @@ describe('Can hot reload CSS Module without losing state', () => { }) describe('CSS Module Composes Usage (Basic)', () => { - // This is a very bad feature. Do not use it. - const appDir = join(fixturesDir, 'composes-basic') - - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - }) - - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".index_className__OLWEh{background:red;color:#ff0}.index_subClass__Z_IFg{background:blue}"` - ) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-basic') + + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + }) + + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".index_className__OLWEh{background:red;color:#ff0}.index_subClass__Z_IFg{background:blue}"` + ) + }) }) }) describe('CSS Module Composes Usage (External)', () => { - // This is a very bad feature. Do not use it. - const appDir = join(fixturesDir, 'composes-external') - - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - }) - - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".other_className__A8aN2{background:red;color:#ff0}.index_subClass__Z_IFg{background:blue}"` - ) + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + // This is a very bad feature. Do not use it. + const appDir = join(fixturesDir, 'composes-external') + + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + }) + + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".other_className__A8aN2{background:red;color:#ff0}.index_subClass__Z_IFg{background:blue}"` + ) + }) }) }) diff --git a/test/integration/scss/test/basic-global-support.test.js b/test/integration/scss/test/basic-global-support.test.js index e254d42897eef..c73ee38686cf1 100644 --- a/test/integration/scss/test/basic-global-support.test.js +++ b/test/integration/scss/test/basic-global-support.test.js @@ -7,274 +7,296 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../..', 'scss-fixtures') -describe('Basic Global Support', () => { - const appDir = join(fixturesDir, 'single-global') +describe('Basic Global Support scss', () => { + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'single-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Basic Module Include Paths Support', () => { - const appDir = join(fixturesDir, 'basic-module-include-paths') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'basic-module-include-paths') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Basic Module Prepend Data Support', () => { - const appDir = join(fixturesDir, 'basic-module-prepend-data') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'basic-module-prepend-data') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Basic Global Support with src/ dir', () => { - const appDir = join(fixturesDir, 'single-global-src') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'single-global-src') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( - 'color:red' - ) + expect(cssFiles.length).toBe(1) + expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain( + 'color:red' + ) + }) }) }) describe('Multi Global Support', () => { - const appDir = join(fixturesDir, 'multi-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'multi-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".red-text{color:red}.blue-text{color:blue}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".red-text{color:red}.blue-text{color:blue}"`) + }) }) }) describe('Nested @import() Global Support', () => { - const appDir = join(fixturesDir, 'nested-global') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'nested-global') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".red-text{color:purple;font-weight:bolder;color:red}.blue-text{color:orange;font-weight:bolder;color:blue}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".red-text{color:purple;font-weight:bolder;color:red}.blue-text{color:orange;font-weight:bolder;color:blue}"` + ) + }) }) }) // Tests css ordering describe('Multi Global Support (reversed)', () => { - const appDir = join(fixturesDir, 'multi-global-reversed') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'multi-global-reversed') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".blue-text{color:blue}.red-text{color:red}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".blue-text{color:blue}.red-text{color:red}"`) + }) }) }) describe('Good CSS Import from node_modules', () => { - const appDir = join(fixturesDir, 'npm-import') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'npm-import') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(/nprogress/) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(/nprogress/) + }) }) }) describe('Good Nested CSS Import from node_modules', () => { - const appDir = join(fixturesDir, 'npm-import-nested') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'npm-import-nested') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) - it(`should've emitted a single CSS file`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted a single CSS file`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot( - `".other{color:blue}.test{color:red}"` - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot(`".other{color:blue}.test{color:red}"`) + }) }) }) describe('CSS Import from node_modules', () => { - const appDir = join(fixturesDir, 'npm-import-bad') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + const appDir = join(fixturesDir, 'npm-import-bad') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it('should fail the build', async () => { - const { code, stderr } = await nextBuild(appDir, [], { stderr: true }) + it('should fail the build', async () => { + const { code, stderr } = await nextBuild(appDir, [], { stderr: true }) - expect(code).toBe(0) - expect(stderr).not.toMatch(/Can't resolve '[^']*?nprogress[^']*?'/) - expect(stderr).not.toMatch(/Build error occurred/) + expect(code).toBe(0) + expect(stderr).not.toMatch(/Can't resolve '[^']*?nprogress[^']*?'/) + expect(stderr).not.toMatch(/Build error occurred/) + }) }) }) diff --git a/test/integration/scss/test/basic-scss.test.js b/test/integration/scss/test/basic-scss.test.js index c442cbedfc099..d1ed47970b63a 100644 --- a/test/integration/scss/test/basic-scss.test.js +++ b/test/integration/scss/test/basic-scss.test.js @@ -28,26 +28,33 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should be a friendly error successfully', async () => { - const { code, stderr } = await nextBuild(appDir, [], { - env: { NODE_OPTIONS: shellQuote([`--require`, mockFile]) }, - stderr: true, - }) - let cleanScssErrMsg = - '\n\n' + - './styles/global.scss\n' + - "To use Next.js' built-in Sass support, you first need to install `sass`.\n" + - 'Run `npm i sass` or `yarn add sass` inside your workspace.\n' + - '\n' + - 'Learn more: https://nextjs.org/docs/messages/install-sass\n' - - expect(code).toBe(1) - expect(stderr).toContain('Failed to compile.') - expect(stderr).toContain(cleanScssErrMsg) - expect(stderr).not.toContain('css-loader') - expect(stderr).not.toContain('sass-loader') - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should be a friendly error successfully', + async () => { + const { code, stderr } = await nextBuild(appDir, [], { + env: { NODE_OPTIONS: shellQuote([`--require`, mockFile]) }, + stderr: true, + }) + let cleanScssErrMsg = + '\n\n' + + './styles/global.scss\n' + + "To use Next.js' built-in Sass support, you first need to install `sass`.\n" + + 'Run `npm i sass` or `yarn add sass` inside your workspace.\n' + + '\n' + + 'Learn more: https://nextjs.org/docs/messages/install-sass\n' + + // eslint-disable-next-line + expect(code).toBe(1) + // eslint-disable-next-line + expect(stderr).toContain('Failed to compile.') + // eslint-disable-next-line + expect(stderr).toContain(cleanScssErrMsg) + // eslint-disable-next-line + expect(stderr).not.toContain('css-loader') + // eslint-disable-next-line + expect(stderr).not.toContain('sass-loader') + } + ) }) describe('CSS Compilation and Prefixing', () => { diff --git a/test/integration/scss/test/scss-loader-handling.test.js b/test/integration/scss/test/scss-loader-handling.test.js index c30d9925bb122..27728a2286e61 100644 --- a/test/integration/scss/test/scss-loader-handling.test.js +++ b/test/integration/scss/test/scss-loader-handling.test.js @@ -7,21 +7,25 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../..', 'scss-fixtures') -describe('SCSS Support', () => { +describe('SCSS Support loader handling', () => { describe('CSS URL via `file-loader`', () => { const appDir = join(fixturesDir, 'url-global') beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -60,14 +64,18 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -108,14 +116,18 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -154,14 +166,18 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -200,14 +216,18 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -229,14 +249,18 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(code).toBe(0) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) it(`should've emitted expected files`, async () => { const cssFolder = join(appDir, '.next/static/css') @@ -258,12 +282,15 @@ describe('SCSS Support', () => { beforeAll(async () => { await remove(join(appDir, '.next')) }) - - it('should compile successfully', async () => { - const { stdout } = await nextBuild(appDir, [], { - stdout: true, - }) - expect(stdout).toMatch(/Compiled successfully/) - }) + ;(process.env.TURBOPACK ? it.skip : it)( + 'should compile successfully', + async () => { + const { stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + // eslint-disable-next-line + expect(stdout).toMatch(/Compiled successfully/) + } + ) }) }) diff --git a/test/integration/url/test/index.test.js b/test/integration/url/test/index.test.js index ac7f5a0a325ba..9ae807bbf0edd 100644 --- a/test/integration/url/test/index.test.js +++ b/test/integration/url/test/index.test.js @@ -20,63 +20,64 @@ jest.setTimeout(1000 * 60 * 2) const appDir = join(__dirname, '../') for (const dev of [false, true]) { - describe(`Handle new URL asset references in next ${ - dev ? 'dev' : 'build' - }`, () => { - let appPort - let app - beforeAll(async () => { - await fs.remove(join(appDir, '.next')) - if (dev) { - appPort = await findPort() - app = await launchApp(appDir, appPort) - } else { - await nextBuild(appDir) - appPort = await findPort() - app = await nextStart(appDir, appPort) - } - }) - afterAll(() => killApp(app)) - - const expectedServer = - /Hello \/_next\/static\/media\/vercel\.[0-9a-f]{8}\.png\+\/_next\/static\/media\/vercel\.[0-9a-f]{8}\.png/ - const expectedClient = new RegExp( - expectedServer.source.replace(//g, '') - ) - - for (const page of ['/static', '/ssr', '/ssg']) { - it(`should render the ${page} page`, async () => { - const html = await renderViaHTTP(appPort, page) - expect(html).toMatch(expectedServer) - }) - - it(`should client-render the ${page} page`, async () => { - let browser - try { - browser = await webdriver(appPort, page) - await check(() => getBrowserBodyText(browser), expectedClient) - } finally { - await browser.close() + ;(process.env.TURBOPACK && !dev ? describe.skip : describe)( + `Handle new URL asset references in next ${dev ? 'dev' : 'build'}`, + () => { + let appPort + let app + beforeAll(async () => { + await fs.remove(join(appDir, '.next')) + if (dev) { + appPort = await findPort() + app = await launchApp(appDir, appPort) + } else { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) } }) - } + afterAll(() => killApp(app)) - it('should respond on size api', async () => { - const data = await fetchViaHTTP(appPort, '/api/size').then( - (res) => res.ok && res.json() + const expectedServer = + /Hello \/_next\/static\/media\/vercel\.[0-9a-f]{8}\.png\+\/_next\/static\/media\/vercel\.[0-9a-f]{8}\.png/ + const expectedClient = new RegExp( + expectedServer.source.replace(//g, '') ) - expect(data).toEqual({ size: 30079 }) - }) + for (const page of ['/static', '/ssr', '/ssg']) { + it(`should render the ${page} page`, async () => { + const html = await renderViaHTTP(appPort, page) + expect(html).toMatch(expectedServer) + }) - it('should respond on basename api', async () => { - const data = await fetchViaHTTP(appPort, '/api/basename').then( - (res) => res.ok && res.json() - ) + it(`should client-render the ${page} page`, async () => { + let browser + try { + browser = await webdriver(appPort, page) + await check(() => getBrowserBodyText(browser), expectedClient) + } finally { + await browser.close() + } + }) + } + + it('should respond on size api', async () => { + const data = await fetchViaHTTP(appPort, '/api/size').then( + (res) => res.ok && res.json() + ) + + expect(data).toEqual({ size: 30079 }) + }) + + it('should respond on basename api', async () => { + const data = await fetchViaHTTP(appPort, '/api/basename').then( + (res) => res.ok && res.json() + ) - expect(data).toEqual({ - basename: expect.stringMatching(/^vercel\.[0-9a-f]{8}\.png$/), + expect(data).toEqual({ + basename: expect.stringMatching(/^vercel\.[0-9a-f]{8}\.png$/), + }) }) - }) - }) + } + ) }