fixes to allow lazy compilation for import() (#32441)
This commit is contained in:
parent
67545bd654
commit
ee220730db
42 changed files with 690 additions and 375 deletions
|
@ -12,6 +12,7 @@ examples/with-jest/**
|
|||
examples/with-mobx-state-tree/**
|
||||
examples/with-mobx/**
|
||||
packages/next/bundles/webpack/packages/*.runtime.js
|
||||
packages/next/bundles/webpack/packages/lazy-compilation-*.js
|
||||
packages/next/compiled/**/*
|
||||
packages/react-refresh-utils/**/*.js
|
||||
packages/react-dev-overlay/lib/**
|
||||
|
|
|
@ -3,6 +3,7 @@ node_modules
|
|||
**/_next/**
|
||||
**/dist/**
|
||||
packages/next/bundles/webpack/packages/*.runtime.js
|
||||
packages/next/bundles/webpack/packages/lazy-compilation-*.js
|
||||
packages/next/compiled/**
|
||||
packages/react-refresh-utils/**/*.js
|
||||
packages/react-refresh-utils/**/*.d.ts
|
||||
|
|
|
@ -79,6 +79,9 @@ pub struct TransformOptions {
|
|||
#[serde(default)]
|
||||
pub is_development: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub is_server: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub styled_components: Option<styled_components::Config>,
|
||||
|
||||
|
@ -113,7 +116,12 @@ pub fn custom_before_pass(file: Arc<SourceFile>, opts: &TransformOptions) -> imp
|
|||
},
|
||||
Optional::new(next_ssg::next_ssg(), !opts.disable_next_ssg),
|
||||
amp_attributes::amp_attributes(),
|
||||
next_dynamic::next_dynamic(file.name.clone(), opts.pages_dir.clone()),
|
||||
next_dynamic::next_dynamic(
|
||||
opts.is_development,
|
||||
opts.is_server,
|
||||
file.name.clone(),
|
||||
opts.pages_dir.clone()
|
||||
),
|
||||
Optional::new(
|
||||
page_config::page_config(opts.is_development, opts.is_page_file),
|
||||
!opts.disable_page_config
|
||||
|
|
|
@ -5,8 +5,8 @@ use swc_atoms::js_word;
|
|||
use swc_common::{FileName, DUMMY_SP};
|
||||
use swc_ecmascript::ast::{
|
||||
ArrayLit, ArrowExpr, BinExpr, BinaryOp, BlockStmtOrExpr, CallExpr, Expr, ExprOrSpread,
|
||||
ExprOrSuper, Ident, ImportDecl, ImportSpecifier, KeyValueProp, Lit, MemberExpr, ObjectLit, Prop,
|
||||
PropName, PropOrSpread, Str, StrKind,
|
||||
ExprOrSuper, Ident, ImportDecl, ImportSpecifier, KeyValueProp, Lit, MemberExpr, ObjectLit,
|
||||
Prop, PropName, PropOrSpread, Str, StrKind,
|
||||
};
|
||||
use swc_ecmascript::utils::{
|
||||
ident::{Id, IdentLike},
|
||||
|
@ -14,8 +14,15 @@ use swc_ecmascript::utils::{
|
|||
};
|
||||
use swc_ecmascript::visit::{Fold, FoldWith};
|
||||
|
||||
pub fn next_dynamic(filename: FileName, pages_dir: Option<PathBuf>) -> impl Fold {
|
||||
pub fn next_dynamic(
|
||||
is_development: bool,
|
||||
is_server: bool,
|
||||
filename: FileName,
|
||||
pages_dir: Option<PathBuf>,
|
||||
) -> impl Fold {
|
||||
NextDynamicPatcher {
|
||||
is_development,
|
||||
is_server,
|
||||
pages_dir,
|
||||
filename,
|
||||
dynamic_bindings: vec![],
|
||||
|
@ -26,6 +33,8 @@ pub fn next_dynamic(filename: FileName, pages_dir: Option<PathBuf>) -> impl Fold
|
|||
|
||||
#[derive(Debug)]
|
||||
struct NextDynamicPatcher {
|
||||
is_development: bool,
|
||||
is_server: bool,
|
||||
pages_dir: Option<PathBuf>,
|
||||
filename: FileName,
|
||||
dynamic_bindings: Vec<Id>,
|
||||
|
@ -81,14 +90,17 @@ impl Fold for NextDynamicPatcher {
|
|||
} else if expr.args.len() > 2 {
|
||||
HANDLER.with(|handler| {
|
||||
handler
|
||||
.struct_span_err(identifier.span, "next/dynamic only accepts 2 arguments")
|
||||
.struct_span_err(
|
||||
identifier.span,
|
||||
"next/dynamic only accepts 2 arguments",
|
||||
)
|
||||
.emit()
|
||||
});
|
||||
return expr;
|
||||
}
|
||||
if expr.args.len() == 2 {
|
||||
match &*expr.args[1].expr {
|
||||
Expr::Object(_) => {},
|
||||
Expr::Object(_) => {}
|
||||
_ => {
|
||||
HANDLER.with(|handler| {
|
||||
handler
|
||||
|
@ -107,31 +119,77 @@ impl Fold for NextDynamicPatcher {
|
|||
expr.args[0].expr = expr.args[0].expr.clone().fold_with(self);
|
||||
self.is_next_dynamic_first_arg = false;
|
||||
|
||||
|
||||
if let None = self.dynamically_imported_specifier {
|
||||
return expr;
|
||||
}
|
||||
|
||||
// dev client or server:
|
||||
// loadableGenerated: {
|
||||
// webpack: () => [require.resolveWeak('../components/hello')],
|
||||
// modules:
|
||||
// ["/project/src/file-being-transformed.js -> " + '../components/hello'] }
|
||||
|
||||
// prod client
|
||||
// loadableGenerated: {
|
||||
// webpack: () => [require.resolveWeak('../components/hello')],
|
||||
let generated = Box::new(Expr::Object(ObjectLit {
|
||||
span: DUMMY_SP,
|
||||
props: vec![
|
||||
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
props: if self.is_development || self.is_server {
|
||||
vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident::new("modules".into(), DUMMY_SP)),
|
||||
value: Box::new(Expr::Array(ArrayLit {
|
||||
elems: vec![Some(ExprOrSpread {
|
||||
expr: Box::new(Expr::Bin(BinExpr {
|
||||
span: DUMMY_SP,
|
||||
op: BinaryOp::Add,
|
||||
left: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
value: format!(
|
||||
"{} -> ",
|
||||
rel_filename(
|
||||
self.pages_dir.as_deref(),
|
||||
&self.filename
|
||||
)
|
||||
)
|
||||
.into(),
|
||||
span: DUMMY_SP,
|
||||
kind: StrKind::Synthesized {},
|
||||
has_escape: false,
|
||||
}))),
|
||||
right: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
value: self
|
||||
.dynamically_imported_specifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into(),
|
||||
span: DUMMY_SP,
|
||||
kind: StrKind::Normal {
|
||||
contains_quote: false,
|
||||
},
|
||||
has_escape: false,
|
||||
}))),
|
||||
})),
|
||||
spread: None,
|
||||
})],
|
||||
span: DUMMY_SP,
|
||||
})),
|
||||
})))]
|
||||
} else {
|
||||
vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident::new("webpack".into(), DUMMY_SP)),
|
||||
value: Box::new(Expr::Arrow(ArrowExpr {
|
||||
params: vec![],
|
||||
body: BlockStmtOrExpr::Expr(Box::new(Expr::Array(ArrayLit {
|
||||
elems: vec![Some(ExprOrSpread {
|
||||
expr: Box::new(Expr::Call(CallExpr {
|
||||
callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr {
|
||||
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(Ident {
|
||||
callee: ExprOrSuper::Expr(Box::new(Expr::Member(
|
||||
MemberExpr {
|
||||
obj: ExprOrSuper::Expr(Box::new(
|
||||
Expr::Ident(Ident {
|
||||
sym: js_word!("require"),
|
||||
span: DUMMY_SP,
|
||||
optional: false,
|
||||
}))),
|
||||
}),
|
||||
)),
|
||||
prop: Box::new(Expr::Ident(Ident {
|
||||
sym: "resolveWeak".into(),
|
||||
span: DUMMY_SP,
|
||||
|
@ -139,7 +197,8 @@ impl Fold for NextDynamicPatcher {
|
|||
})),
|
||||
computed: false,
|
||||
span: DUMMY_SP,
|
||||
}))),
|
||||
},
|
||||
))),
|
||||
args: vec![ExprOrSpread {
|
||||
expr: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
value: self
|
||||
|
@ -167,47 +226,12 @@ impl Fold for NextDynamicPatcher {
|
|||
return_type: None,
|
||||
type_params: None,
|
||||
})),
|
||||
}))),
|
||||
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident::new("modules".into(), DUMMY_SP)),
|
||||
value: Box::new(Expr::Array(ArrayLit {
|
||||
elems: vec![Some(ExprOrSpread {
|
||||
expr: Box::new(Expr::Bin(BinExpr {
|
||||
span: DUMMY_SP,
|
||||
op: BinaryOp::Add,
|
||||
left: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
value: format!(
|
||||
"{} -> ",
|
||||
rel_filename(self.pages_dir.as_deref(), &self.filename)
|
||||
)
|
||||
.into(),
|
||||
span: DUMMY_SP,
|
||||
kind: StrKind::Synthesized {},
|
||||
has_escape: false,
|
||||
}))),
|
||||
right: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
value: self
|
||||
.dynamically_imported_specifier
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into(),
|
||||
span: DUMMY_SP,
|
||||
kind: StrKind::Normal {
|
||||
contains_quote: false,
|
||||
})))]
|
||||
},
|
||||
has_escape: false,
|
||||
}))),
|
||||
})),
|
||||
spread: None,
|
||||
})],
|
||||
span: DUMMY_SP,
|
||||
})),
|
||||
}))),
|
||||
],
|
||||
}));
|
||||
|
||||
let mut props = vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
let mut props =
|
||||
vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident::new("loadableGenerated".into(), DUMMY_SP)),
|
||||
value: generated,
|
||||
})))];
|
||||
|
|
|
@ -32,6 +32,8 @@ fn next_dynamic_errors(input: PathBuf) {
|
|||
syntax(),
|
||||
&|_tr| {
|
||||
next_dynamic(
|
||||
true,
|
||||
false,
|
||||
FileName::Real(PathBuf::from("/some-project/src/some-file.js")),
|
||||
Some("/some-project/src".into()),
|
||||
)
|
||||
|
|
|
@ -33,17 +33,33 @@ fn amp_attributes_fixture(input: PathBuf) {
|
|||
|
||||
#[fixture("tests/fixture/next-dynamic/**/input.js")]
|
||||
fn next_dynamic_fixture(input: PathBuf) {
|
||||
let output = input.parent().unwrap().join("output.js");
|
||||
let output_dev = input.parent().unwrap().join("output-dev.js");
|
||||
let output_prod = input.parent().unwrap().join("output-prod.js");
|
||||
test_fixture(
|
||||
syntax(),
|
||||
&|_tr| {
|
||||
next_dynamic(
|
||||
true,
|
||||
false,
|
||||
FileName::Real(PathBuf::from("/some-project/src/some-file.js")),
|
||||
Some("/some-project/src".into()),
|
||||
)
|
||||
},
|
||||
&input,
|
||||
&output,
|
||||
&output_dev,
|
||||
);
|
||||
test_fixture(
|
||||
syntax(),
|
||||
&|_tr| {
|
||||
next_dynamic(
|
||||
false,
|
||||
false,
|
||||
FileName::Real(PathBuf::from("/some-project/src/some-file.js")),
|
||||
Some("/some-project/src".into()),
|
||||
)
|
||||
},
|
||||
&input,
|
||||
&output_prod,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import dynamic1 from 'next/dynamic'
|
||||
import dynamic2 from 'next/dynamic'
|
||||
const DynamicComponent1 = dynamic1(() => import('../components/hello1'), {
|
||||
loadableGenerated: {
|
||||
modules: ['some-file.js -> ' + '../components/hello1'],
|
||||
},
|
||||
})
|
||||
const DynamicComponent2 = dynamic2(() => import('../components/hello2'), {
|
||||
loadableGenerated: {
|
||||
modules: ['some-file.js -> ' + '../components/hello2'],
|
||||
},
|
||||
})
|
|
@ -3,12 +3,10 @@ import dynamic2 from 'next/dynamic'
|
|||
const DynamicComponent1 = dynamic1(() => import('../components/hello1'), {
|
||||
loadableGenerated: {
|
||||
webpack: () => [require.resolveWeak('../components/hello1')],
|
||||
modules: ['some-file.js -> ' + '../components/hello1'],
|
||||
},
|
||||
})
|
||||
const DynamicComponent2 = dynamic2(() => import('../components/hello2'), {
|
||||
loadableGenerated: {
|
||||
webpack: () => [require.resolveWeak('../components/hello2')],
|
||||
modules: ['some-file.js -> ' + '../components/hello2'],
|
||||
},
|
||||
})
|
|
@ -0,0 +1,8 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
import somethingElse from 'something-else'
|
||||
const DynamicComponent = dynamic(() => import('../components/hello'), {
|
||||
loadableGenerated: {
|
||||
modules: ['some-file.js -> ' + '../components/hello'],
|
||||
},
|
||||
})
|
||||
somethingElse.dynamic('should not be transformed')
|
|
@ -3,7 +3,6 @@ import somethingElse from 'something-else'
|
|||
const DynamicComponent = dynamic(() => import('../components/hello'), {
|
||||
loadableGenerated: {
|
||||
webpack: () => [require.resolveWeak('../components/hello')],
|
||||
modules: ['some-file.js -> ' + '../components/hello'],
|
||||
},
|
||||
})
|
||||
somethingElse.dynamic('should not be transformed')
|
|
@ -0,0 +1,6 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
const DynamicComponent = dynamic(() => import('../components/hello'), {
|
||||
loadableGenerated: {
|
||||
modules: ['some-file.js -> ' + '../components/hello'],
|
||||
},
|
||||
})
|
|
@ -2,6 +2,5 @@ import dynamic from 'next/dynamic'
|
|||
const DynamicComponent = dynamic(() => import('../components/hello'), {
|
||||
loadableGenerated: {
|
||||
webpack: () => [require.resolveWeak('../components/hello')],
|
||||
modules: ['some-file.js -> ' + '../components/hello'],
|
||||
},
|
||||
})
|
|
@ -0,0 +1,10 @@
|
|||
import dynamic from "next/dynamic";
|
||||
const DynamicComponentWithCustomLoading = dynamic(()=>import("../components/hello")
|
||||
, {
|
||||
loadableGenerated: {
|
||||
modules: [
|
||||
"some-file.js -> " + "../components/hello"
|
||||
]
|
||||
},
|
||||
loading: ()=><p >...</p>
|
||||
});
|
|
@ -5,10 +5,6 @@ const DynamicComponentWithCustomLoading = dynamic(()=>import("../components/hell
|
|||
webpack: ()=>[
|
||||
require.resolveWeak("../components/hello")
|
||||
]
|
||||
,
|
||||
modules: [
|
||||
"some-file.js -> " + "../components/hello"
|
||||
]
|
||||
},
|
||||
loading: ()=><p >...</p>
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import dynamic from "next/dynamic";
|
||||
const DynamicComponent = dynamic(()=>handleImport(import("./components/hello"))
|
||||
, {
|
||||
loadableGenerated: {
|
||||
modules: [
|
||||
"some-file.js -> " + "./components/hello"
|
||||
]
|
||||
},
|
||||
loading: ()=>null
|
||||
,
|
||||
ssr: false
|
||||
});
|
|
@ -5,10 +5,6 @@ const DynamicComponent = dynamic(()=>handleImport(import("./components/hello"))
|
|||
webpack: ()=>[
|
||||
require.resolveWeak("./components/hello")
|
||||
]
|
||||
,
|
||||
modules: [
|
||||
"some-file.js -> " + "./components/hello"
|
||||
]
|
||||
},
|
||||
loading: ()=>null
|
||||
,
|
|
@ -55,6 +55,7 @@ fn test(input: &Path, minify: bool) {
|
|||
pages_dir: None,
|
||||
is_page_file: false,
|
||||
is_development: true,
|
||||
is_server: false,
|
||||
styled_components: Some(assert_json("{}")),
|
||||
remove_console: None,
|
||||
react_remove_properties: None,
|
||||
|
|
|
@ -168,7 +168,16 @@ export default function ({
|
|||
options.node.properties.push(
|
||||
t.objectProperty(
|
||||
t.identifier('loadableGenerated'),
|
||||
t.objectExpression([
|
||||
t.objectExpression(
|
||||
state.file.opts.caller?.isDev ||
|
||||
state.file.opts.caller?.isServer
|
||||
? [
|
||||
t.objectProperty(
|
||||
t.identifier('modules'),
|
||||
t.arrayExpression(dynamicKeys)
|
||||
),
|
||||
]
|
||||
: [
|
||||
t.objectProperty(
|
||||
t.identifier('webpack'),
|
||||
t.arrowFunctionExpression(
|
||||
|
@ -186,11 +195,8 @@ export default function ({
|
|||
)
|
||||
)
|
||||
),
|
||||
t.objectProperty(
|
||||
t.identifier('modules'),
|
||||
t.arrayExpression(dynamicKeys)
|
||||
),
|
||||
])
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -141,6 +141,7 @@ export function getLoaderSWCOptions({
|
|||
disableNextSsg: true,
|
||||
disablePageConfig: true,
|
||||
isDevelopment: development,
|
||||
isServer,
|
||||
pagesDir,
|
||||
isPageFile,
|
||||
env: {
|
||||
|
@ -165,6 +166,7 @@ export function getLoaderSWCOptions({
|
|||
: {}),
|
||||
disableNextSsg: !isPageFile,
|
||||
isDevelopment: development,
|
||||
isServer,
|
||||
pagesDir,
|
||||
isPageFile,
|
||||
}
|
||||
|
|
|
@ -1402,6 +1402,7 @@ export default async function getBaseWebpackConfig(
|
|||
runtimeAsset: hasConcurrentFeatures
|
||||
? `server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`
|
||||
: undefined,
|
||||
dev,
|
||||
}),
|
||||
targetWeb && new DropClientPage(),
|
||||
config.outputFileTracing &&
|
||||
|
@ -1733,6 +1734,21 @@ export default async function getBaseWebpackConfig(
|
|||
devtoolRevertWarning(originalDevtool)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
const webpack5Config = webpackConfig as webpack5.Configuration
|
||||
|
||||
// disable lazy compilation of entries as next.js has it's own method here
|
||||
if (webpack5Config.experiments?.lazyCompilation === true) {
|
||||
webpack5Config.experiments.lazyCompilation = {
|
||||
entries: false,
|
||||
}
|
||||
} else if (
|
||||
typeof webpack5Config.experiments?.lazyCompilation === 'object' &&
|
||||
webpack5Config.experiments.lazyCompilation.entries !== false
|
||||
) {
|
||||
webpack5Config.experiments.lazyCompilation.entries = false
|
||||
}
|
||||
|
||||
if (typeof (webpackConfig as any).then === 'function') {
|
||||
console.warn(
|
||||
'> Promise returned in next config. https://nextjs.org/docs/messages/promise-in-next-config'
|
||||
|
|
|
@ -53,7 +53,8 @@ function getChunkGroupFromBlock(
|
|||
function buildManifest(
|
||||
_compiler: webpack.Compiler,
|
||||
compilation: webpack.compilation.Compilation,
|
||||
pagesDir: string
|
||||
pagesDir: string,
|
||||
dev: boolean
|
||||
) {
|
||||
let manifest: { [k: string]: { id: string | number; files: string[] } } = {}
|
||||
|
||||
|
@ -125,7 +126,7 @@ function buildManifest(
|
|||
// next/dynamic so they are loaded by the same technique
|
||||
|
||||
// add the id and files to the manifest
|
||||
const id = getModuleId(compilation, module)
|
||||
const id = dev ? key : getModuleId(compilation, module)
|
||||
manifest[key] = { id, files: Array.from(files) }
|
||||
}
|
||||
}
|
||||
|
@ -146,19 +147,27 @@ export class ReactLoadablePlugin {
|
|||
private filename: string
|
||||
private pagesDir: string
|
||||
private runtimeAsset?: string
|
||||
private dev: boolean
|
||||
|
||||
constructor(opts: {
|
||||
filename: string
|
||||
pagesDir: string
|
||||
runtimeAsset?: string
|
||||
dev: boolean
|
||||
}) {
|
||||
this.filename = opts.filename
|
||||
this.pagesDir = opts.pagesDir
|
||||
this.runtimeAsset = opts.runtimeAsset
|
||||
this.dev = opts.dev
|
||||
}
|
||||
|
||||
createAssets(compiler: any, compilation: any, assets: any) {
|
||||
const manifest = buildManifest(compiler, compilation, this.pagesDir)
|
||||
const manifest = buildManifest(
|
||||
compiler,
|
||||
compilation,
|
||||
this.pagesDir,
|
||||
this.dev
|
||||
)
|
||||
// @ts-ignore: TODO: remove when webpack 5 is stable
|
||||
assets[this.filename] = new sources.RawSource(
|
||||
JSON.stringify(manifest, null, 2)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* global __resourceQuery */
|
||||
|
||||
"use strict";
|
||||
|
||||
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
|
||||
exports.keepAlive = function (options) {
|
||||
var data = options.data;
|
||||
var onError = options.onError;
|
||||
var active = options.active;
|
||||
var module = options.module;
|
||||
var response;
|
||||
var request = (
|
||||
urlBase.startsWith("https") ? require("https") : require("http")
|
||||
).request(
|
||||
urlBase + data,
|
||||
{
|
||||
agent: false,
|
||||
headers: { accept: "text/event-stream" }
|
||||
},
|
||||
function (res) {
|
||||
response = res;
|
||||
response.on("error", errorHandler);
|
||||
if (!active && !module.hot) {
|
||||
console.log(
|
||||
"Hot Module Replacement is not enabled. Waiting for process restart..."
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
function errorHandler(err) {
|
||||
err.message =
|
||||
"Problem communicating active modules to the server: " + err.message;
|
||||
onError(err);
|
||||
}
|
||||
request.on("error", errorHandler);
|
||||
request.end();
|
||||
return function () {
|
||||
response.destroy();
|
||||
};
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
/* global __resourceQuery */
|
||||
|
||||
"use strict";
|
||||
|
||||
if (typeof EventSource !== "function") {
|
||||
throw new Error(
|
||||
"Environment doesn't support lazy compilation (requires EventSource)"
|
||||
);
|
||||
}
|
||||
|
||||
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
|
||||
var activeEventSource;
|
||||
var activeKeys = new Map();
|
||||
var errorHandlers = new Set();
|
||||
|
||||
var updateEventSource = function updateEventSource() {
|
||||
if (activeEventSource) activeEventSource.close();
|
||||
if (activeKeys.size) {
|
||||
activeEventSource = new EventSource(
|
||||
urlBase + Array.from(activeKeys.keys()).join("@")
|
||||
);
|
||||
activeEventSource.onerror = function (event) {
|
||||
errorHandlers.forEach(function (onError) {
|
||||
onError(
|
||||
new Error(
|
||||
"Problem communicating active modules to the server: " +
|
||||
event.message +
|
||||
" " +
|
||||
event.filename +
|
||||
":" +
|
||||
event.lineno +
|
||||
":" +
|
||||
event.colno +
|
||||
" " +
|
||||
event.error
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
} else {
|
||||
activeEventSource = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
exports.keepAlive = function (options) {
|
||||
var data = options.data;
|
||||
var onError = options.onError;
|
||||
var active = options.active;
|
||||
var module = options.module;
|
||||
errorHandlers.add(onError);
|
||||
var value = activeKeys.get(data) || 0;
|
||||
activeKeys.set(data, value + 1);
|
||||
if (value === 0) {
|
||||
updateEventSource();
|
||||
}
|
||||
if (!active && !module.hot) {
|
||||
console.log(
|
||||
"Hot Module Replacement is not enabled. Waiting for process restart..."
|
||||
);
|
||||
}
|
||||
|
||||
return function () {
|
||||
errorHandlers.delete(onError);
|
||||
setTimeout(function () {
|
||||
var value = activeKeys.get(data);
|
||||
if (value === 1) {
|
||||
activeKeys.delete(data);
|
||||
updateEventSource();
|
||||
} else {
|
||||
activeKeys.set(data, value - 1);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
};
|
|
@ -1,67 +1,6 @@
|
|||
/******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ 685:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("http");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 687:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("https");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __nccwpck_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ var threw = true;
|
||||
/******/ try {
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __nccwpck_require__);
|
||||
/******/ threw = false;
|
||||
/******/ } finally {
|
||||
/******/ if(threw) delete __webpack_module_cache__[moduleId];
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ /* webpack/runtime/compat */
|
||||
/******/
|
||||
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||
!function() {
|
||||
var exports = __webpack_exports__;
|
||||
var __resourceQuery = "";
|
||||
/* global __resourceQuery */
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
|
||||
exports.keepAlive = function (options) {
|
||||
|
@ -71,7 +10,7 @@ exports.keepAlive = function (options) {
|
|||
var module = options.module;
|
||||
var response;
|
||||
var request = (
|
||||
urlBase.startsWith("https") ? __nccwpck_require__(687) : __nccwpck_require__(685)
|
||||
urlBase.startsWith("https") ? require("https") : require("http")
|
||||
).request(
|
||||
urlBase + data,
|
||||
{
|
||||
|
@ -99,8 +38,3 @@ exports.keepAlive = function (options) {
|
|||
response.destroy();
|
||||
};
|
||||
};
|
||||
|
||||
}();
|
||||
module.exports = __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
|
@ -1,18 +1,6 @@
|
|||
/******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ /* webpack/runtime/compat */
|
||||
/******/
|
||||
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
|
||||
!function() {
|
||||
var exports = __webpack_exports__;
|
||||
var __resourceQuery = "";
|
||||
/* global __resourceQuery */
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
if (typeof EventSource !== "function") {
|
||||
throw new Error(
|
||||
|
@ -84,8 +72,3 @@ exports.keepAlive = function (options) {
|
|||
}, 1000);
|
||||
};
|
||||
};
|
||||
|
||||
}();
|
||||
module.exports = __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
|
@ -95,14 +95,9 @@ function createLoadableComponent(loadFn, options) {
|
|||
}
|
||||
|
||||
// Client only
|
||||
if (
|
||||
!initialized &&
|
||||
typeof window !== 'undefined' &&
|
||||
typeof opts.webpack === 'function' &&
|
||||
typeof require.resolveWeak === 'function' &&
|
||||
!opts.suspense
|
||||
) {
|
||||
const moduleIds = opts.webpack()
|
||||
if (!initialized && typeof window !== 'undefined' && !opts.suspense) {
|
||||
const moduleIds = opts.webpack ? opts.webpack() : opts.modules
|
||||
if (moduleIds) {
|
||||
READY_INITIALIZERS.push((ids) => {
|
||||
for (const moduleId of moduleIds) {
|
||||
if (ids.indexOf(moduleId) !== -1) {
|
||||
|
@ -111,6 +106,7 @@ function createLoadableComponent(loadFn, options) {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function LoadableImpl(props, ref) {
|
||||
init()
|
||||
|
|
|
@ -1007,7 +1007,6 @@ export async function ncc(task, opts) {
|
|||
'ncc_unistore',
|
||||
'ncc_web_vitals',
|
||||
'ncc_webpack_bundle5',
|
||||
'ncc_webpack_bundle_packages',
|
||||
'ncc_webpack_sources1',
|
||||
'ncc_webpack_sources3',
|
||||
'ncc_ws',
|
||||
|
@ -1021,6 +1020,7 @@ export async function ncc(task, opts) {
|
|||
],
|
||||
opts
|
||||
)
|
||||
await task.parallel(['ncc_webpack_bundle_packages'], opts)
|
||||
await task.parallel(['ncc_babel_bundle_packages'], opts)
|
||||
await task.parallel(['copy_react_server_dom_webpack'])
|
||||
}
|
||||
|
|
8
scripts/check-pre-compiled.bat
Normal file
8
scripts/check-pre-compiled.bat
Normal file
|
@ -0,0 +1,8 @@
|
|||
copy node_modules\webpack5\lib\hmr\HotModuleReplacement.runtime.js packages\next\bundles\webpack\packages\
|
||||
copy node_modules\webpack5\lib\hmr\JavascriptHotModuleReplacement.runtime.js packages\next\bundles\webpack\packages\
|
||||
copy node_modules\webpack5\hot\lazy-compilation-node.js packages\next\bundles\webpack\packages\
|
||||
copy node_modules\webpack5\hot\lazy-compilation-web.js packages\next\bundles\webpack\packages\
|
||||
yarn --cwd packages/next ncc-compiled
|
||||
|
||||
rem Make sure to exit with 1 if there are changes after running ncc-compiled
|
||||
rem step to ensure we get any changes committed
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
cp node_modules/webpack5/lib/hmr/HotModuleReplacement.runtime.js packages/next/bundles/webpack/packages/
|
||||
cp node_modules/webpack5/lib/hmr/JavascriptHotModuleReplacement.runtime.js packages/next/bundles/webpack/packages/
|
||||
cp node_modules/webpack5/hot/lazy-compilation-node.js packages/next/bundles/webpack/packages/
|
||||
cp node_modules/webpack5/hot/lazy-compilation-web.js packages/next/bundles/webpack/packages/
|
||||
yarn --cwd packages/next ncc-compiled
|
||||
|
||||
# Make sure to exit with 1 if there are changes after running ncc-compiled
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('basic next/dynamic usage', () => {
|
|||
const $ = await get$('/docs/dynamic/ssr')
|
||||
// Make sure the client side knows it has to wait for the bundle
|
||||
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
|
||||
'./components/hello1.js'
|
||||
'dynamic/ssr.js -> ../../components/hello1'
|
||||
)
|
||||
expect($('body').text()).toMatch(/Hello World 1/)
|
||||
})
|
||||
|
@ -41,7 +41,7 @@ describe('basic next/dynamic usage', () => {
|
|||
const $ = await get$('/docs/dynamic/function')
|
||||
// Make sure the client side knows it has to wait for the bundle
|
||||
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
|
||||
'./components/hello1.js'
|
||||
'dynamic/function.js -> ../../components/hello1'
|
||||
)
|
||||
expect($('body').text()).toMatch(/Hello World 1/)
|
||||
})
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('basic next/dynamic usage', () => {
|
|||
const $ = await get$('/dynamic/ssr')
|
||||
// Make sure the client side knows it has to wait for the bundle
|
||||
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
|
||||
'./components/hello1.js'
|
||||
'dynamic/ssr.js -> ../../components/hello1'
|
||||
)
|
||||
expect($('body').text()).toMatch(/Hello World 1/)
|
||||
})
|
||||
|
@ -38,7 +38,7 @@ describe('basic next/dynamic usage', () => {
|
|||
const $ = await get$('/dynamic/function')
|
||||
// Make sure the client side knows it has to wait for the bundle
|
||||
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
|
||||
'./components/hello1.js'
|
||||
'dynamic/function.js -> ../../components/hello1'
|
||||
)
|
||||
expect($('body').text()).toMatch(/Hello World 1/)
|
||||
})
|
||||
|
|
|
@ -7,7 +7,6 @@ import { killApp, findPort, launchApp, check } from 'next-test-utils'
|
|||
|
||||
const appDir = join(__dirname, '../')
|
||||
const appPage = join(appDir, 'pages/_app.js')
|
||||
const indexPage = join(appDir, 'pages/index.js')
|
||||
const documentPage = join(appDir, 'pages/_document.js')
|
||||
|
||||
let appPort
|
||||
|
@ -21,10 +20,8 @@ describe('_app/_document add HMR', () => {
|
|||
afterAll(() => killApp(app))
|
||||
|
||||
it('should HMR when _app is added', async () => {
|
||||
let indexContent = await fs.readFile(indexPage)
|
||||
try {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
|
||||
try {
|
||||
const html = await browser.eval('document.documentElement.innerHTML')
|
||||
expect(html).not.toContain('custom _app')
|
||||
expect(html).toContain('index page')
|
||||
|
@ -50,16 +47,19 @@ describe('_app/_document add HMR', () => {
|
|||
: html
|
||||
}, 'success')
|
||||
} finally {
|
||||
await fs.writeFile(indexPage, indexContent)
|
||||
await fs.remove(appPage)
|
||||
await check(async () => {
|
||||
const html = await browser.eval('document.documentElement.innerHTML')
|
||||
return !html.includes('custom _app') && html.includes('index page')
|
||||
? 'restored'
|
||||
: html
|
||||
}, 'restored')
|
||||
}
|
||||
})
|
||||
|
||||
it('should HMR when _document is added', async () => {
|
||||
let indexContent = await fs.readFile(indexPage)
|
||||
try {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
|
||||
try {
|
||||
const html = await browser.eval('document.documentElement.innerHTML')
|
||||
expect(html).not.toContain('custom _document')
|
||||
expect(html).toContain('index page')
|
||||
|
@ -101,8 +101,13 @@ describe('_app/_document add HMR', () => {
|
|||
: html
|
||||
}, 'success')
|
||||
} finally {
|
||||
await fs.writeFile(indexPage, indexContent)
|
||||
await fs.remove(documentPage)
|
||||
await check(async () => {
|
||||
const html = await browser.eval('document.documentElement.innerHTML')
|
||||
return !html.includes('custom _document') && html.includes('index page')
|
||||
? 'restored'
|
||||
: html
|
||||
}, 'restored')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
3
test/integration/next-dynamic-lazy-compilation/.babelrc
Normal file
3
test/integration/next-dynamic-lazy-compilation/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["next/babel"]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export default 'foobar'
|
|
@ -0,0 +1,3 @@
|
|||
export default () => {
|
||||
return '4'
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import something from '../apples'
|
||||
export default () => {
|
||||
// have to do something with module so it is not tree shaken
|
||||
console.log(something)
|
||||
return '1'
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default () => {
|
||||
return '3'
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import something from '../apples'
|
||||
export default () => {
|
||||
// have to do something with module so it is not tree shaken
|
||||
console.log(something)
|
||||
return '2'
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
webpack(config, { isServer, dev }) {
|
||||
if (!isServer && dev) {
|
||||
config.experiments.lazyCompilation = true
|
||||
}
|
||||
return config
|
||||
},
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
import { useState, useEffect } from 'react'
|
||||
import FourDirect from '../components/four'
|
||||
|
||||
const One = dynamic(() => import('../components/one'))
|
||||
const Two = dynamic(() => import('../components/two'))
|
||||
const Three = dynamic(() => import('../components/three'))
|
||||
const Four = dynamic(() => import('../components/four'))
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.caughtErrors = ''
|
||||
const origError = console.error
|
||||
|
||||
console.error = function (...args) {
|
||||
window.caughtErrors += args.join(' ')
|
||||
origError(...args)
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE_HYDRATION =
|
||||
typeof document !== 'undefined' && document.getElementById('foo').innerHTML
|
||||
|
||||
const Index = () => {
|
||||
const [firstRender, setFirstRender] = useState('the-server-value')
|
||||
const [beforeHydration, setBeforeHydration] = useState(
|
||||
'the-second-server-value'
|
||||
)
|
||||
useEffect(() => {
|
||||
setFirstRender(document.getElementById('foo').innerHTML)
|
||||
setBeforeHydration(BEFORE_HYDRATION)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="foo">
|
||||
Index
|
||||
<One />
|
||||
<Two />
|
||||
<Three />
|
||||
<Four />
|
||||
<FourDirect />
|
||||
</div>
|
||||
<div id="first-render">{firstRender}</div>
|
||||
<div id="before-hydration">{beforeHydration}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Index
|
|
@ -0,0 +1,73 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
renderViaHTTP,
|
||||
findPort,
|
||||
launchApp,
|
||||
killApp,
|
||||
runNextCommand,
|
||||
nextServer,
|
||||
startApp,
|
||||
stopApp,
|
||||
} from 'next-test-utils'
|
||||
|
||||
let app
|
||||
let appPort
|
||||
let server
|
||||
const appDir = join(__dirname, '../')
|
||||
|
||||
function runTests() {
|
||||
it('should render server value', async () => {
|
||||
const html = await renderViaHTTP(appPort, '/')
|
||||
expect(html).toMatch(/the-server-value/i)
|
||||
expect(html).toMatch(/the-second-server-value/i)
|
||||
})
|
||||
|
||||
it('should render dynamic server rendered values before hydration', async () => {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
const text = await browser.elementByCss('#before-hydration').text()
|
||||
|
||||
expect(text).toBe('Index<!-- -->1<!-- -->2<!-- -->3<!-- -->4<!-- -->4')
|
||||
expect(await browser.eval('window.caughtErrors')).toBe('')
|
||||
})
|
||||
|
||||
it('should render dynamic server rendered values on client mount', async () => {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
const text = await browser.elementByCss('#first-render').text()
|
||||
|
||||
expect(text).toBe('Index<!-- -->1<!-- -->2<!-- -->3<!-- -->4<!-- -->4')
|
||||
expect(await browser.eval('window.caughtErrors')).toBe('')
|
||||
})
|
||||
}
|
||||
|
||||
describe('next/dynamic', () => {
|
||||
describe('dev mode', () => {
|
||||
beforeAll(async () => {
|
||||
appPort = await findPort()
|
||||
app = await launchApp(appDir, appPort)
|
||||
})
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests(true)
|
||||
})
|
||||
|
||||
describe('production mode', () => {
|
||||
beforeAll(async () => {
|
||||
await runNextCommand(['build', appDir])
|
||||
|
||||
app = nextServer({
|
||||
dir: appDir,
|
||||
dev: false,
|
||||
quiet: true,
|
||||
})
|
||||
|
||||
server = await startApp(app)
|
||||
appPort = server.address().port
|
||||
})
|
||||
afterAll(() => stopApp(server))
|
||||
|
||||
runTests()
|
||||
})
|
||||
})
|
|
@ -193,7 +193,7 @@ describe('next-babel-loader', () => {
|
|||
expect(
|
||||
code.replace(/modules: \[".*?"/, 'modules:["/path/to/page"')
|
||||
).toMatchInlineSnapshot(
|
||||
`"var _jsxFileName = \\"index.js\\";import React from \\"react\\";var __jsx = React.createElement;import dynamic from 'next/dynamic';const Comp = dynamic(() => import('comp'), { loadableGenerated: { webpack: () => [require.resolveWeak('comp')], modules:[\\"/path/to/page\\" + 'comp'] }});export default function Page(props) { return __jsx(Comp, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 7, columnNumber: 18 } });}"`
|
||||
`"var _jsxFileName = \\"index.js\\";import React from \\"react\\";var __jsx = React.createElement;import dynamic from 'next/dynamic';const Comp = dynamic(() => import('comp'), { loadableGenerated: { webpack: () => [require.resolveWeak('comp')] }});export default function Page(props) { return __jsx(Comp, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 7, columnNumber: 18 } });}"`
|
||||
)
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue