@thomas.f/prnext 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/build/action-exports.mjs +107 -0
- package/build/actions.mjs +214 -0
- package/build/app-config.mjs +138 -0
- package/build/app-routing.mjs +119 -0
- package/build/app-static.mjs +158 -0
- package/build/app.mjs +536 -0
- package/build/cache-components.mjs +91 -0
- package/build/cache-handlers.mjs +54 -0
- package/build/check.mjs +40 -0
- package/build/compiler.mjs +222 -0
- package/build/config.mjs +193 -0
- package/build/css-modules.mjs +114 -0
- package/build/custom-routes.mjs +363 -0
- package/build/dev-worker.mjs +18 -0
- package/build/dev.mjs +76 -0
- package/build/directory-cache.mjs +29 -0
- package/build/dynamic.mjs +125 -0
- package/build/edge.mjs +141 -0
- package/build/font-data.LICENSE.txt +25 -0
- package/build/font-data.json +1 -0
- package/build/font-files.mjs +221 -0
- package/build/fonts.mjs +131 -0
- package/build/framework-imports.mjs +17 -0
- package/build/i18n.mjs +39 -0
- package/build/images-config.mjs +62 -0
- package/build/images.mjs +86 -0
- package/build/incremental.mjs +76 -0
- package/build/index.mjs +356 -0
- package/build/loader-cache.mjs +69 -0
- package/build/metadata-images.mjs +72 -0
- package/build/metadata-routes.mjs +77 -0
- package/build/middleware.mjs +231 -0
- package/build/migrate.mjs +289 -0
- package/build/module-resolution.mjs +78 -0
- package/build/native-discovery.mjs +134 -0
- package/build/native-packages.mjs +105 -0
- package/build/npm-packages.mjs +110 -0
- package/build/og.mjs +14 -0
- package/build/page-extensions.mjs +12 -0
- package/build/passthrough-loader.cjs +4 -0
- package/build/precompress.mjs +122 -0
- package/build/scan.mjs +178 -0
- package/build/script-workers.mjs +25 -0
- package/build/server-actions-config.mjs +27 -0
- package/build/source-maps.mjs +34 -0
- package/build/standalone-config.mjs +19 -0
- package/build/standalone-runner.mjs +21 -0
- package/build/standalone-worker.mjs +5 -0
- package/build/standalone.mjs +240 -0
- package/build/static-export.mjs +93 -0
- package/build/styles.mjs +174 -0
- package/build/transform-cache.mjs +50 -0
- package/build/transform.mjs +155 -0
- package/build/turbopack-conditions.mjs +59 -0
- package/build/webpack-bridge.cjs +3 -0
- package/build/webpack-graph.mjs +256 -0
- package/build/webpack-loader.cjs +9 -0
- package/cli.mjs +210 -0
- package/compat/app-client-page.cjs +32 -0
- package/compat/app-context.cjs +40 -0
- package/compat/app-context.d.cts +20 -0
- package/compat/app-dynamic.cjs +34 -0
- package/compat/app-error-boundary.cjs +53 -0
- package/compat/app-global-error-boundary.cjs +34 -0
- package/compat/app-layout-context.cjs +27 -0
- package/compat/app-navigation-boundary.cjs +47 -0
- package/compat/app.cjs +33 -0
- package/compat/app.d.cts +9 -0
- package/compat/cache-handlers.cjs +223 -0
- package/compat/cache-life.cjs +34 -0
- package/compat/cache.cjs +99 -0
- package/compat/cache.d.cts +36 -0
- package/compat/compat/router.d.cts +2 -0
- package/compat/compat-router.cjs +7 -0
- package/compat/constants.cjs +8 -0
- package/compat/constants.d.cts +7 -0
- package/compat/cookies.cjs +129 -0
- package/compat/data-cache.cjs +389 -0
- package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
- package/compat/dist/shared/lib/router-context.d.cts +1 -0
- package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
- package/compat/document.cjs +73 -0
- package/compat/document.d.cts +41 -0
- package/compat/draft.cjs +28 -0
- package/compat/dynamic-bailout.cjs +13 -0
- package/compat/dynamic.cjs +119 -0
- package/compat/dynamic.d.cts +32 -0
- package/compat/error.cjs +36 -0
- package/compat/error.d.cts +15 -0
- package/compat/fetch.d.cts +10 -0
- package/compat/font/google.d.cts +1 -0
- package/compat/font/local.d.cts +2 -0
- package/compat/font-google.cjs +2 -0
- package/compat/font-google.d.cts +1944 -0
- package/compat/font-local.cjs +5 -0
- package/compat/font-local.d.cts +3 -0
- package/compat/font-types.d.cts +25 -0
- package/compat/head.cjs +180 -0
- package/compat/head.d.cts +3 -0
- package/compat/headers.cjs +89 -0
- package/compat/headers.d.cts +26 -0
- package/compat/image-config.cjs +3 -0
- package/compat/image-imports.d.ts +12 -0
- package/compat/image-shared.cjs +62 -0
- package/compat/image.cjs +64 -0
- package/compat/image.d.cts +35 -0
- package/compat/incremental-cache.cjs +186 -0
- package/compat/index.d.ts +159 -0
- package/compat/instant-samples.cjs +39 -0
- package/compat/instant.cjs +13 -0
- package/compat/link.cjs +68 -0
- package/compat/link.d.cts +17 -0
- package/compat/locale.cjs +30 -0
- package/compat/metadata.d.cts +77 -0
- package/compat/navigation-controls.cjs +27 -0
- package/compat/navigation-server.cjs +25 -0
- package/compat/navigation.cjs +52 -0
- package/compat/navigation.d.cts +28 -0
- package/compat/next-app-router-context.cjs +6 -0
- package/compat/next-router-context.cjs +3 -0
- package/compat/og.cjs +32 -0
- package/compat/og.d.cts +1 -0
- package/compat/paths.cjs +33 -0
- package/compat/preview.cjs +41 -0
- package/compat/rewrite.cjs +41 -0
- package/compat/route-request.cjs +64 -0
- package/compat/router.cjs +137 -0
- package/compat/router.d.cts +58 -0
- package/compat/script-context.cjs +5 -0
- package/compat/script-loader.cjs +135 -0
- package/compat/script.cjs +69 -0
- package/compat/script.d.cts +17 -0
- package/compat/server-primitives.cjs +109 -0
- package/compat/server.cjs +13 -0
- package/compat/server.d.cts +37 -0
- package/compat/static-generation.cjs +108 -0
- package/compat/use-cache.cjs +129 -0
- package/index.mjs +1 -0
- package/native/platforms.json +6 -0
- package/native/resolve.mjs +81 -0
- package/package.json +127 -0
- package/release/verify.mjs +59 -0
- package/runtime/action-client.mjs +19 -0
- package/runtime/action-crypto.mjs +138 -0
- package/runtime/action-reference.mjs +2 -0
- package/runtime/action-server.mjs +161 -0
- package/runtime/action-ssr.mjs +5 -0
- package/runtime/api.mjs +326 -0
- package/runtime/app-client.mjs +539 -0
- package/runtime/app-content.mjs +8 -0
- package/runtime/app-errors.mjs +12 -0
- package/runtime/app-instant.mjs +30 -0
- package/runtime/app-metadata.mjs +410 -0
- package/runtime/app-partial-model.mjs +228 -0
- package/runtime/app-partial.mjs +130 -0
- package/runtime/app-prefetch.mjs +34 -0
- package/runtime/app-recovery.mjs +87 -0
- package/runtime/app-render.mjs +544 -0
- package/runtime/app-routing-access.mjs +143 -0
- package/runtime/app-routing-context.mjs +35 -0
- package/runtime/app-routing-state.mjs +61 -0
- package/runtime/app-routing.mjs +323 -0
- package/runtime/app-segments.mjs +22 -0
- package/runtime/app-static-params.mjs +120 -0
- package/runtime/app-static.mjs +86 -0
- package/runtime/app-stream-channel.mjs +92 -0
- package/runtime/app-stream-html.mjs +279 -0
- package/runtime/build-directory.mjs +48 -0
- package/runtime/cache-arguments.mjs +33 -0
- package/runtime/cache-components-rsc.mjs +64 -0
- package/runtime/cache-request.mjs +23 -0
- package/runtime/client-navigation.mjs +40 -0
- package/runtime/dev-client.mjs +129 -0
- package/runtime/dev-file-changes.mjs +110 -0
- package/runtime/document.mjs +96 -0
- package/runtime/edge.mjs +105 -0
- package/runtime/env.mjs +68 -0
- package/runtime/fetch-cache.mjs +379 -0
- package/runtime/font-preload.mjs +4 -0
- package/runtime/http.mjs +190 -0
- package/runtime/incremental-cache.mjs +83 -0
- package/runtime/metadata-route.mjs +83 -0
- package/runtime/middleware.mjs +186 -0
- package/runtime/module-loader.mjs +21 -0
- package/runtime/navigation.mjs +16 -0
- package/runtime/og-loader.mjs +17 -0
- package/runtime/pages-client.mjs +197 -0
- package/runtime/pages-error-props.mjs +6 -0
- package/runtime/pages-errors.mjs +85 -0
- package/runtime/pages-initial-props.mjs +43 -0
- package/runtime/pages-navigation.mjs +565 -0
- package/runtime/pages-paths.mjs +80 -0
- package/runtime/pages-revalidate.mjs +33 -0
- package/runtime/partial-artifact-cache.mjs +37 -0
- package/runtime/partial-template.mjs +174 -0
- package/runtime/profile.mjs +9 -0
- package/runtime/profiles.json +47 -0
- package/runtime/render.mjs +304 -0
- package/runtime/request-transport.mjs +41 -0
- package/runtime/request-work.mjs +7 -0
- package/runtime/route-static.mjs +117 -0
- package/runtime/rsc-worker.mjs +426 -0
- package/runtime/script-html.mjs +61 -0
- package/runtime/stream-select.mjs +25 -0
- package/runtime/stream-utils.mjs +127 -0
- package/runtime/transport.mjs +108 -0
- package/runtime/watch-project.mjs +130 -0
- package/runtime/worker-artifacts.mjs +22 -0
- package/runtime/worker-metadata.mjs +31 -0
- package/runtime/worker.mjs +197 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import {build as esbuild, transform} from 'esbuild';
|
|
2
|
+
import {mkdir, mkdtemp, readFile, writeFile, rm, realpath} from 'node:fs/promises';
|
|
3
|
+
import {createHash, randomUUID} from 'node:crypto';
|
|
4
|
+
import {createRequire} from 'node:module';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { cachedAsyncTransform } from './transform-cache.mjs';
|
|
8
|
+
import {inlineSourceMap} from './source-maps.mjs';
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const {active} = require('./webpack-bridge.cjs');
|
|
12
|
+
const loaderPath = fileURLToPath(new URL('./webpack-loader.cjs', import.meta.url));
|
|
13
|
+
const hash = value => createHash('sha256').update(value).digest('hex').slice(0, 20);
|
|
14
|
+
const slash = value => value.replaceAll(path.sep, '/');
|
|
15
|
+
const safeAsset = name => { if (path.isAbsolute(name) || /[\\\0?#]/.test(name) || name.split('/').some(part => !part || part === '.' || part === '..')) throw new Error(`Unsafe webpack asset path: ${name}`); };
|
|
16
|
+
|
|
17
|
+
/** Webpack owns the actual module/chunk graph. PRNext's resolve/load transforms
|
|
18
|
+
* remain authoritative for React boundaries and the Node/Edge/browser targets.
|
|
19
|
+
*/
|
|
20
|
+
export async function webpackGraph(options, {state, config, target}) {
|
|
21
|
+
const loads = [], starts = [], ends = [], disposes = [];
|
|
22
|
+
let output;
|
|
23
|
+
const scaffold = {...options, entryPoints:undefined, stdin:{contents:'',resolveDir:state.root},
|
|
24
|
+
outfile:undefined, outdir:path.join(state.stage,'.webpack-resolver'), outbase:undefined, splitting:false, write:false, metafile:false,
|
|
25
|
+
plugins:[{name:'prnext-webpack-resolver',async setup(builder) {
|
|
26
|
+
for (const plugin of options.plugins || []) await plugin.setup({...builder,
|
|
27
|
+
onLoad(settings,callback){loads.push({settings,callback,configuredLoaders:plugin.name==='prnext-webpack-loaders',framework:/^prnext-(?:pages-compatibility|app-|edge)/.test(plugin.name)});},
|
|
28
|
+
onStart(callback){starts.push(callback);}, onEnd(callback){ends.push(callback);}, onDispose(callback){disposes.push(callback);},
|
|
29
|
+
});
|
|
30
|
+
builder.onStart(async()=>{
|
|
31
|
+
for (const start of starts) await start();
|
|
32
|
+
output = await compileGraph(builder, loads, options, {state,config,target});
|
|
33
|
+
for (const end of ends) await end(output);
|
|
34
|
+
});
|
|
35
|
+
}}]};
|
|
36
|
+
try { await esbuild(scaffold); return output; }
|
|
37
|
+
finally { for (const dispose of disposes) await dispose(); }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function compileGraph(resolver, loads, options, {state,config,target}) {
|
|
41
|
+
const webpack = (await import('webpack')).default;
|
|
42
|
+
const bridgeId = randomUUID();
|
|
43
|
+
await mkdir(path.join(state.stage,'.entries'),{recursive:true});
|
|
44
|
+
const virtualDirectory = await realpath(await mkdtemp(path.join(state.stage,'.entries/webpack-')));
|
|
45
|
+
const temporary = await mkdtemp(path.join(state.stage,'.webpack-output-'));
|
|
46
|
+
const outdir = options.outdir || (options.outfile ? path.dirname(options.outfile) : temporary);
|
|
47
|
+
const actualOut = options.write === false ? temporary : outdir;
|
|
48
|
+
const extension = options.outExtension?.['.js'] || '.js';
|
|
49
|
+
const modules = new Map();
|
|
50
|
+
const projectRequire = createRequire(path.join(state.root,'package.json'));
|
|
51
|
+
let compiler;
|
|
52
|
+
const entries = options.stdin ? {entry:path.join(virtualDirectory,'entry.js')}
|
|
53
|
+
: Array.isArray(options.entryPoints) ? Object.fromEntries(options.entryPoints.map(file=>[path.basename(file,path.extname(file)),file])) : options.entryPoints;
|
|
54
|
+
if (options.stdin) {
|
|
55
|
+
const file = entries.entry;
|
|
56
|
+
await writeFile(file,options.stdin.contents);
|
|
57
|
+
modules.set(file,{path:file,namespace:'file',resolveDir:options.stdin.resolveDir || state.root,stdin:true});
|
|
58
|
+
}
|
|
59
|
+
const identify = record => record.namespace === 'file' ? slash(path.relative(state.root,record.path)) : record.namespace+':'+slash(path.relative(state.root,record.path));
|
|
60
|
+
const lookup = resource => modules.get(resource) || modules.get(resource?.split('?')[0]) || {path:resource?.split('?')[0],namespace:'file',suffix:resource?.includes('?')?'?'+resource.split('?').slice(1).join('?'):''};
|
|
61
|
+
const load = async(record,source,webpackContext,sourceMap) => {
|
|
62
|
+
if (record.stdin) return {contents:source,loader:options.stdin.loader || 'js',resolveDir:record.resolveDir};
|
|
63
|
+
for (const {settings,callback,framework,configuredLoaders} of loads) {
|
|
64
|
+
if (configuredLoaders && record.inline) continue;
|
|
65
|
+
if (settings.namespace && settings.namespace !== record.namespace) continue;
|
|
66
|
+
settings.filter.lastIndex = 0;
|
|
67
|
+
if (!(record.customLoader && framework && settings.namespace==='file') && !settings.filter.test(record.path)) continue;
|
|
68
|
+
const result = await callback({...record,webpackContext,prnextSourceMap:sourceMap,prnextSource:record.namespace==='file' && (!configuredLoaders || record.nativeLoaders)?(configuredLoaders?source.toString():inlineSourceMap(source.toString(),sourceMap)):undefined});
|
|
69
|
+
if (result) {
|
|
70
|
+
if (result.errors?.length) throw new Error(result.errors.map(error=>error.text).join('\n'));
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {contents:inlineSourceMap(source.toString(),sourceMap),loader:options.loader?.[path.extname(record.path)] || ({'.json':'json','.ts':'ts','.tsx':'tsx','.jsx':'jsx','.css':'css'}[path.extname(record.path)] || 'js')};
|
|
75
|
+
};
|
|
76
|
+
active.set(bridgeId,{async transform(context,source,sourceMap) {
|
|
77
|
+
const record = modules.get(context._module.rawRequest) || lookup(context.resource);
|
|
78
|
+
if (record.namespace==='file' && context.loaders.length===1) state.devInputs?.record(record.path,source);
|
|
79
|
+
// CSS url() inputs are binary resources, not JavaScript export wrappers.
|
|
80
|
+
if (record.cssAsset) return {code:source};
|
|
81
|
+
record.nativeLoaders = context.loaders.length > 1;
|
|
82
|
+
if (context.loaders.length > 1 && record.namespace==='file') state.loaded.set(record.path,{source:source.toString()});
|
|
83
|
+
const result = await load(record,source,context,sourceMap);
|
|
84
|
+
record.resolveDir = result.resolveDir || record.resolveDir || path.dirname(record.path);
|
|
85
|
+
for (const file of result.watchFiles || []) context.addDependency(file);
|
|
86
|
+
for (const directory of result.watchDirs || []) context.addContextDependency(directory);
|
|
87
|
+
for (const warning of result.warnings || []) context.emitWarning(new Error(warning.text));
|
|
88
|
+
const contents = Buffer.from(result.contents);
|
|
89
|
+
if (['css','json'].includes(result.loader)) return {code:contents.toString()};
|
|
90
|
+
if (result.loader === 'file') {
|
|
91
|
+
const name = path.basename(record.path,path.extname(record.path))+'-'+hash(contents)+path.extname(record.path);
|
|
92
|
+
context.emitFile(name,contents);
|
|
93
|
+
return {code:`export default ${JSON.stringify(state.assetBase+'/'+name)}`};
|
|
94
|
+
}
|
|
95
|
+
const flightAdapter=/[/\\]react-server-dom-webpack[/\\].*client\.browser/.test(record.path);
|
|
96
|
+
const settings={loader:result.loader || 'js',sourcefile:record.path,
|
|
97
|
+
jsx:options.jsx || 'automatic',target:options.target || 'es2022',define:{...options.define,...(flightAdapter?{
|
|
98
|
+
__webpack_require__:'globalThis.__webpack_require__',__webpack_chunk_load__:'globalThis.__webpack_chunk_load__',__webpack_get_script_filename__:'globalThis.__webpack_get_script_filename__',
|
|
99
|
+
}:{})},
|
|
100
|
+
sourcemap:options.sourcemap ? 'external' : false,format:undefined,
|
|
101
|
+
};
|
|
102
|
+
const code = JSON.parse(await cachedAsyncTransform('webpack-syntax',contents.toString(),settings,async()=>JSON.stringify(await transform(contents.toString(),settings))));
|
|
103
|
+
return {code:code.code,map:code.map ? JSON.parse(code.map) : sourceMap};
|
|
104
|
+
}});
|
|
105
|
+
const naming = template => (template || '[name]').replaceAll('[hash]','[contenthash:16]');
|
|
106
|
+
// A changed synchronous chunk may leave webpack's runtime bytes unchanged.
|
|
107
|
+
// Give each published build its own module cache; React's dev singletons
|
|
108
|
+
// still retain their identities across the new runtime instance.
|
|
109
|
+
const runtime = 'webpack-runtime-'+hash(JSON.stringify([state.stage,Object.keys(entries),options.conditions,target]));
|
|
110
|
+
try {
|
|
111
|
+
compiler = webpack({name:target,context:state.root,mode:state.dev?'development':'production',
|
|
112
|
+
...(config.watchOptions ? {watchOptions:config.watchOptions} : {}),
|
|
113
|
+
target:target==='node'?'node22':['web','es2022'],entry:entries,cache:false,devtool:options.sourcemap?'source-map':false,
|
|
114
|
+
experiments:{outputModule:true,css:true},externalsType:'module',
|
|
115
|
+
output:{path:actualOut,filename:options.outfile?path.basename(options.outfile):naming(options.entryNames)+extension,
|
|
116
|
+
chunkFilename:naming(options.chunkNames || 'chunk-[hash]')+extension,cssFilename:naming(options.entryNames)+'.css',
|
|
117
|
+
cssChunkFilename:'style-[contenthash:16].css',module:true,library:{type:'module'},chunkFormat:'module',chunkLoading:'import',
|
|
118
|
+
publicPath:target==='node'?'./':state.assetBase+'/',environment:{module:true,dynamicImport:true},clean:false},
|
|
119
|
+
performance:false,
|
|
120
|
+
optimization:{minimize:!!options.minify,minimizer:[{apply(compiler){
|
|
121
|
+
compiler.hooks.compilation.tap('PRNextMinify',compilation=>{
|
|
122
|
+
compilation.hooks.processAssets.tapPromise({name:'PRNextMinify',stage:webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE},async()=>{
|
|
123
|
+
// Sequential native esbuild transforms avoid a Terser worker pool.
|
|
124
|
+
for(const asset of compilation.getAssets()){
|
|
125
|
+
if(!/\.(?:m?js|css)$/.test(asset.name))continue;
|
|
126
|
+
const original=asset.source.sourceAndMap(),source=original.source.toString();
|
|
127
|
+
const settings={loader:asset.name.endsWith('.css')?'css':'js',minify:true,target:options.target || 'es2022',sourcefile:asset.name,sourcemap:options.sourcemap?'external':false,legalComments:'eof'};
|
|
128
|
+
const minified=JSON.parse(await cachedAsyncTransform('webpack-minify',source,settings,async()=>JSON.stringify(await transform(source,settings))));
|
|
129
|
+
compilation.updateAsset(asset.name,minified.map?new webpack.sources.SourceMapSource(minified.code,asset.name,JSON.parse(minified.map),source,original.map,true):new webpack.sources.RawSource(minified.code));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}}],runtimeChunk:options.splitting?{name:runtime}:false,
|
|
134
|
+
splitChunks:options.splitting?{chunks:'all',minSize:0,cacheGroups:{default:false,defaultVendors:false,shared:{minChunks:2,test:module=>!module.type.startsWith('css'),name:false}}}:false},
|
|
135
|
+
resolve:{alias:config.resolution.resolveAlias || {},conditionNames:[...(options.conditions || []),'...'],extensions:config.resolution.resolveExtensions || ['.tsx','.ts','.jsx','.js','.mjs','.json'],fullySpecified:false},
|
|
136
|
+
module:{rules:[{test:/./,type:'javascript/auto',enforce:'post',use:[{loader:loaderPath,options:{bridge:bridgeId}}]},
|
|
137
|
+
{test:/\.json$/,type:'json'},{test:/\.(?:css|scss|sass)$/,type:'css'},
|
|
138
|
+
{dependency:'url',type:'asset/resource'}]},
|
|
139
|
+
plugins:[...config.graphPlugins,
|
|
140
|
+
...(!options.splitting?[new webpack.optimize.LimitChunkCountPlugin({maxChunks:1})]:[]),
|
|
141
|
+
...(options.banner?.js?[new webpack.BannerPlugin({banner:options.banner.js,raw:true})]:[]),
|
|
142
|
+
...(options.footer?.js?[new webpack.BannerPlugin({banner:options.footer.js,raw:true,footer:true})]:[]),
|
|
143
|
+
{apply(compiler) {
|
|
144
|
+
if (state.devInputs && config.graphPlugins.some(plugin=>plugin.constructor?.name==='ContentlayerWebpackPlugin')) {
|
|
145
|
+
compiler.hooks.beforeCompile.tapPromise({name:'PRNextGeneratedInputs',stage:Number.MAX_SAFE_INTEGER},()=>
|
|
146
|
+
state.generatedInputs ||= state.devInputs.captureDirectory('.contentlayer/generated'));
|
|
147
|
+
}
|
|
148
|
+
compiler.hooks.normalModuleFactory.tap('PRNextFramework',factory=>{
|
|
149
|
+
factory.hooks.beforeResolve.tapPromise({name:'PRNextFramework',stage:10000},async data=>{
|
|
150
|
+
if (!data || modules.has(data.request)) return;
|
|
151
|
+
const importer = lookup(data.contextInfo.issuer);
|
|
152
|
+
const dependency = data.dependencies[0];
|
|
153
|
+
const kind = dependency?.type?.includes('url') ? 'url-token' : dependency?.type==='css @import'?'import-rule':data.dependencyType==='commonjs'?'require-call':dependency?.type==='import()'?'dynamic-import':data.contextInfo.issuer?'import-statement':'entry-point';
|
|
154
|
+
const settings={resolveDir:importer.resolveDir || data.context || state.root,importer:importer.path || '',namespace:importer.namespace || 'file',kind};
|
|
155
|
+
// Let webpack resolve/execute the inline loader chain itself. Only
|
|
156
|
+
// its resource passes through framework resolution.
|
|
157
|
+
const bang=data.request.lastIndexOf('!');
|
|
158
|
+
const inline=bang<0?'':data.request.slice(0,bang+1);
|
|
159
|
+
const request=bang<0?data.request:data.request.slice(bang+1);
|
|
160
|
+
const resolved = await resolver.resolve(request,settings);
|
|
161
|
+
if (resolved.errors.length) throw new Error(resolved.errors.map(error=>error.text).join('\n'));
|
|
162
|
+
if (resolved.external) {
|
|
163
|
+
data.prnextExternal=resolved.path;
|
|
164
|
+
if (dependency?.type==='css url()') {
|
|
165
|
+
data.prnextExternalType='asset';data.prnextExternalMeta={sourceType:'asset-url'};
|
|
166
|
+
} else if (kind==='import-rule') {
|
|
167
|
+
data.prnextExternalType='css-import';
|
|
168
|
+
data.prnextExternalMeta={layer:dependency.layer,supports:dependency.supports,media:dependency.media};
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const record={...resolved,namespace:resolved.namespace || 'file',cssAsset:dependency?.type==='css url()',inline:!!inline,customLoader:!!inline,inlinePrefix:inline.startsWith('!!')?'!!':inline.startsWith('-!')?'-!':inline.startsWith('!')?'!':''};
|
|
173
|
+
let resource=record.path+(record.suffix || '');
|
|
174
|
+
if(record.namespace!=='file'){
|
|
175
|
+
resource=path.join(virtualDirectory,hash(JSON.stringify([record.namespace,record.path,record.suffix]))+(/css$/.test(record.namespace)?'.css':'.js'));
|
|
176
|
+
if(!modules.has(resource))await writeFile(resource,'');
|
|
177
|
+
}
|
|
178
|
+
data.request=inline+resource;
|
|
179
|
+
modules.set(data.request,record);
|
|
180
|
+
});
|
|
181
|
+
factory.hooks.afterResolve.tap({name:'PRNextInlineLoaders',stage:10000},data=>{
|
|
182
|
+
const record=modules.get(data.createData.rawRequest);
|
|
183
|
+
if (!record?.inline) return;
|
|
184
|
+
const selected=(record.pluginData?.prnextLoaders || []).filter(item=>record.inlinePrefix==='!!'?false:record.inlinePrefix==='-!'?item.enforce==='post':record.inlinePrefix==='!'?!!item.enforce:true);
|
|
185
|
+
const resolve=item=>({loader:projectRequire.resolve(item.loader),options:item.options});
|
|
186
|
+
// The framework bridge must run even when !! disables user rules.
|
|
187
|
+
// Normal webpack order: post, inline, normal, pre (right to left).
|
|
188
|
+
data.createData.loaders=[{loader:loaderPath,options:{bridge:bridgeId}},
|
|
189
|
+
...selected.filter(item=>item.enforce==='post').map(resolve),
|
|
190
|
+
...data.createData.loaders.filter(item=>item.loader!==loaderPath),
|
|
191
|
+
...selected.filter(item=>!item.enforce).map(resolve),
|
|
192
|
+
...selected.filter(item=>item.enforce==='pre').map(resolve)];
|
|
193
|
+
// Webpack computed the identity before this hook. Prefix variants
|
|
194
|
+
// must not collapse onto the same module after adding user rules.
|
|
195
|
+
data.createData.request=data.createData.loaders.map(item=>item.loader+(item.options?'?'+JSON.stringify(item.options):'')).join('!')+'!'+data.createData.resource;
|
|
196
|
+
});
|
|
197
|
+
factory.hooks.factorize.tapAsync({name:'PRNextExternals',stage:-10000},(data,done)=>{
|
|
198
|
+
if(data.prnextExternal!==undefined)return done(null,new webpack.ExternalModule(data.prnextExternal,data.prnextExternalType || (target==='node'&&!data.prnextExternal.endsWith('.mjs')?'node-commonjs':'module'),data.request,data.prnextExternalMeta));
|
|
199
|
+
done();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
compiler.hooks.compilation.tap('PRNextOutput',compilation=>{
|
|
203
|
+
compilation.hooks.processAssets.tap({name:'PRNextAssetPaths',stage:webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT},()=>{
|
|
204
|
+
for(const asset of compilation.getAssets())safeAsset(asset.name);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}},
|
|
208
|
+
]});
|
|
209
|
+
const stats=await new Promise((resolve,reject)=>compiler.run((error,stats)=>error?reject(error):resolve(stats)));
|
|
210
|
+
if(stats.hasErrors())throw new Error(stats.toString({all:false,errors:true,errorDetails:true}));
|
|
211
|
+
for(const warning of stats.compilation.warnings){
|
|
212
|
+
const file=warning.module?.resource || warning.module?.identifier?.() || '';
|
|
213
|
+
const location=typeof warning.loc==='string'?warning.loc:warning.loc?.start?`${warning.loc.start.line}:${warning.loc.start.column}`:'';
|
|
214
|
+
console.warn(`[PRNext webpack ${target}] ${file?slash(path.relative(state.root,file)):''}${location?':'+location:''}\n${warning.message}`);
|
|
215
|
+
}
|
|
216
|
+
const compilation=stats.compilation, inputs={}, outputs={}, outputFiles=[];
|
|
217
|
+
const entryFiles=new Map(), styles=new Map(), chunkFiles=new Set();
|
|
218
|
+
for(const chunk of compilation.chunks)for(const file of chunk.files)chunkFiles.add(file);
|
|
219
|
+
for(const [name,entry] of compilation.entrypoints){
|
|
220
|
+
const file=[...entry.getEntrypointChunk().files].find(file=>file.endsWith(extension));
|
|
221
|
+
if(file)entryFiles.set(file,entries[name]);
|
|
222
|
+
const css=entry.getFiles().find(file=>file.endsWith('.css'));if(file&&css)styles.set(file,css);
|
|
223
|
+
}
|
|
224
|
+
for(const module of compilation.modules){
|
|
225
|
+
if(!module.resource)continue;
|
|
226
|
+
const record=lookup(module.resource), imports=[];
|
|
227
|
+
for(const connection of compilation.moduleGraph.getOutgoingConnections(module)){
|
|
228
|
+
if(connection.module?.resource)imports.push({path:identify(lookup(connection.module.resource)),kind:'import-statement'});
|
|
229
|
+
}
|
|
230
|
+
inputs[identify(record)]={bytes:module.originalSource()?.size() || 0,imports};
|
|
231
|
+
}
|
|
232
|
+
const inputSizes=Object.fromEntries(Object.keys(inputs).map(key=>[key,{bytesInOutput:0}]));
|
|
233
|
+
for(const asset of compilation.getAssets()){
|
|
234
|
+
safeAsset(asset.name);
|
|
235
|
+
const contents=await readFile(path.join(actualOut,asset.name));
|
|
236
|
+
const file=path.join(outdir,asset.name), key=slash(path.relative(state.root,file));
|
|
237
|
+
outputs[key]={bytes:contents.length,imports:[],exports:[],inputs:inputSizes,
|
|
238
|
+
...(entryFiles.has(asset.name)?{entryPoint:slash(path.relative(state.root,entryFiles.get(asset.name)))}:{}),
|
|
239
|
+
...(styles.has(asset.name)?{cssBundle:slash(path.relative(state.root,path.join(outdir,styles.get(asset.name))))}:{})};
|
|
240
|
+
outputFiles.push({path:file,contents,get text(){return this.contents.toString();},hash:hash(contents)});
|
|
241
|
+
if(!chunkFiles.has(asset.name)&&!asset.name.endsWith('.map')){
|
|
242
|
+
const destination=path.join(state.stage,'assets',asset.name);
|
|
243
|
+
if(destination!==path.join(actualOut,asset.name)){
|
|
244
|
+
try {if(!(await readFile(destination)).equals(contents))throw new Error(`Conflicting webpack asset: ${asset.name}`);}catch(error){if(error.code!=='ENOENT')throw error;}
|
|
245
|
+
await mkdir(path.dirname(destination),{recursive:true});await writeFile(destination,contents);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const rank = file => entryFiles.has(slash(path.relative(outdir,file.path))) ? 0 : file.path.endsWith(extension) ? 1 : 2;
|
|
250
|
+
outputFiles.sort((a,b)=>rank(a)-rank(b));
|
|
251
|
+
return {errors:[],warnings:[],metafile:{inputs,outputs},...(options.write===false?{outputFiles}:{})};
|
|
252
|
+
} finally {
|
|
253
|
+
try {if(compiler)await new Promise((resolve,reject)=>compiler.close(error=>error?reject(error):resolve()));}
|
|
254
|
+
finally {active.delete(bridgeId);await rm(temporary,{recursive:true,force:true});await rm(virtualDirectory,{recursive:true,force:true});}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const {active} = require('./webpack-bridge.cjs');
|
|
2
|
+
module.exports = function(source, map) {
|
|
3
|
+
const done = this.async();
|
|
4
|
+
const bridge = active.get(this.getOptions().bridge);
|
|
5
|
+
if (!bridge) return done(new Error('PRNext webpack compilation has already closed'));
|
|
6
|
+
this.cacheable(false); // Framework discovery has per-build side effects.
|
|
7
|
+
bridge.transform(this, source, map).then(result => done(null, result.code, result.map), done);
|
|
8
|
+
};
|
|
9
|
+
module.exports.raw = true;
|
package/cli.mjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { readFile, writeFile, rename, rm } from 'node:fs/promises';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { resolveNativeBinary, packageManifest } from './native/resolve.mjs';
|
|
7
|
+
import { readBuildDirectory } from './runtime/build-directory.mjs';
|
|
8
|
+
import { shouldWatchProjectFile } from './runtime/env.mjs';
|
|
9
|
+
|
|
10
|
+
const self = fileURLToPath(import.meta.url);
|
|
11
|
+
const [command = 'help', ...argv] = process.argv.slice(2);
|
|
12
|
+
const help = `PRNext ${packageManifest.version}
|
|
13
|
+
|
|
14
|
+
Use prnext or prn: both names run the same CLI.
|
|
15
|
+
|
|
16
|
+
prnext build [directory] Build React/TypeScript and static HTML
|
|
17
|
+
prnext start [directory] [options] Start native Rust production server
|
|
18
|
+
prnext dev [directory] [options] Rebuild and restart on source changes
|
|
19
|
+
prnext routes [directory] Display compiled routes
|
|
20
|
+
prnext check [directory] [--json] Check an existing Next project before building
|
|
21
|
+
prn migrate [directory] [options] Migrate a Next project's scripts and dependencies
|
|
22
|
+
|
|
23
|
+
Migration options: --dry-run (preview), --no-install (prepare only), --json
|
|
24
|
+
Migration keeps Next.js and backs up package.json and existing lockfiles.
|
|
25
|
+
Simple next dev/build/start scripts become prn commands; custom shell scripts need a manual edit.
|
|
26
|
+
|
|
27
|
+
Server options: --port 3000 --hostname 127.0.0.1 --workers 1
|
|
28
|
+
Production profiles: --profile balanced|speed|memory|classic
|
|
29
|
+
Memory favors lower RAM under concurrency, accepting more waiting and CPU per response.
|
|
30
|
+
Without an option: PRNEXT_PROFILE, legacy PRNEXT_MEMORY_PROFILE=compact, then balanced.
|
|
31
|
+
Classic preserves the former standard settings; standard remains an alias. Legacy compact remains accepted.
|
|
32
|
+
Development includes React Fast Refresh, stylesheet updates and an error overlay.
|
|
33
|
+
Requires Node >=22. Published packages include the native server for supported platforms.
|
|
34
|
+
Rust is only needed when building the framework from source.
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
function done(child) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
child.once('error', reject);
|
|
40
|
+
child.once('exit', (code, signal) => code === 0 ? resolve() : reject(new Error(`Process exited ${signal || code}`)));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function stop(child) {
|
|
44
|
+
if (!child || child.exitCode !== null || child.signalCode) return Promise.resolve();
|
|
45
|
+
return new Promise(resolve => {
|
|
46
|
+
const timer = setTimeout(() => child.kill('SIGKILL'), 5000);
|
|
47
|
+
child.once('exit', () => { clearTimeout(timer); resolve(); });
|
|
48
|
+
child.kill('SIGTERM');
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
if (['help', '--help', '-h'].includes(command)) { console.log(help); return; }
|
|
54
|
+
if (['--version', '-v'].includes(command)) { console.log(packageManifest.version); return; }
|
|
55
|
+
if (command === 'migrate') {
|
|
56
|
+
if (argv.includes('--help') || argv.includes('-h')) { console.log(help); return; }
|
|
57
|
+
const { parseMigrationArgs, migrateProject } = await import('./build/migrate.mjs');
|
|
58
|
+
const { directory, options } = parseMigrationArgs(argv);
|
|
59
|
+
const report = await migrateProject(directory, options);
|
|
60
|
+
if (options.json) console.log(JSON.stringify(report, null, 2));
|
|
61
|
+
else {
|
|
62
|
+
console.log(`PRNext migration ${report.status}: ${report.root}`);
|
|
63
|
+
for (const change of report.changes) console.log(` ${change.field}: ${JSON.stringify(change.before)} -> ${JSON.stringify(change.after)}`);
|
|
64
|
+
for (const backup of report.backups) console.log(`Backup: ${backup}`);
|
|
65
|
+
for (const note of report.notes) console.log(note);
|
|
66
|
+
if (report.error) console.error(report.error);
|
|
67
|
+
if (report.ok && !options.dryRun) console.log(options.install ? 'Next: npm run build, then npm start (or npm run dev).' : 'Next: install dependencies with your package manager, then prn check and prn build.');
|
|
68
|
+
}
|
|
69
|
+
if (!report.ok) process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!['build', 'start', 'dev', 'routes', 'check'].includes(command)) throw new Error(`Unknown command: ${command}\n${help}`);
|
|
73
|
+
const args = [...argv];
|
|
74
|
+
const root = path.resolve(args[0] && !args[0].startsWith('-') ? args.shift() : '.');
|
|
75
|
+
if (command === 'build' || command === 'dev') {
|
|
76
|
+
// Build plugins such as Contentlayer read cwd/INIT_CWD rather than webpack's
|
|
77
|
+
// context. Explicit application directories must behave like `cd app`.
|
|
78
|
+
process.chdir(root);
|
|
79
|
+
process.env.INIT_CWD = root;
|
|
80
|
+
process.env.PWD = root;
|
|
81
|
+
}
|
|
82
|
+
if (command === 'check') {
|
|
83
|
+
if (args.some(arg => arg !== '--json')) throw new Error('check accepts only [directory] and --json');
|
|
84
|
+
const { checkProject } = await import('./build/check.mjs');
|
|
85
|
+
const report = await checkProject(root);
|
|
86
|
+
if (args.includes('--json')) console.log(JSON.stringify(report, null, 2));
|
|
87
|
+
else {
|
|
88
|
+
console.log(`PRNext preflight ${report.ok ? 'passed' : 'failed'}: ${root}`);
|
|
89
|
+
console.log(`${report.routes.length} routes. Installed versions: ${Object.entries(report.versions).map(([name,version])=>`${name}@${version||'missing'}`).join(', ')}`);
|
|
90
|
+
for (const error of report.errors) console.error(error);
|
|
91
|
+
for (const note of report.notes) console.log(note);
|
|
92
|
+
}
|
|
93
|
+
if (!report.ok) process.exitCode = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (command === 'build') {
|
|
97
|
+
const dev = args.includes('--dev');
|
|
98
|
+
if (args.some(arg => arg !== '--dev')) throw new Error('build accepts only [directory] and --dev');
|
|
99
|
+
const envMode = process.env.NODE_ENV === 'test' ? 'test' : undefined;
|
|
100
|
+
process.env.NODE_ENV = dev ? 'development' : 'production';
|
|
101
|
+
const { build } = await import('./build/index.mjs');
|
|
102
|
+
const started = performance.now();
|
|
103
|
+
await build(root, { dev, envMode });
|
|
104
|
+
console.log(`PRNext built ${root} in ${((performance.now() - started) / 1000).toFixed(2)}s`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const binary = await resolveNativeBinary();
|
|
108
|
+
let outputDirectory = await readBuildDirectory(root);
|
|
109
|
+
const launch = () => spawn(binary, [command === 'routes' ? 'routes' : 'start', root,
|
|
110
|
+
...(command === 'routes' ? [] : ['--worker', path.join(root, outputDirectory, 'runtime/worker.mjs')]), ...args], { stdio: 'inherit', env: { ...process.env, NODE_ENV: process.env.NODE_ENV || (command === 'dev' ? 'development' : 'production') } });
|
|
111
|
+
let server;
|
|
112
|
+
let stopping = false;
|
|
113
|
+
let watcher;
|
|
114
|
+
let rebuildChild;
|
|
115
|
+
let timer;
|
|
116
|
+
const devStateFile = path.join(root, '.prnext-dev.json');
|
|
117
|
+
const changedFiles = new Set();
|
|
118
|
+
async function notifyDev(value) {
|
|
119
|
+
const temporary = `${devStateFile}.${process.pid}.tmp`;
|
|
120
|
+
await writeFile(temporary, JSON.stringify(value));
|
|
121
|
+
await rename(temporary, devStateFile);
|
|
122
|
+
}
|
|
123
|
+
const shutdown = async () => {
|
|
124
|
+
if (stopping) return;
|
|
125
|
+
stopping = true;
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
watcher?.close();
|
|
128
|
+
await Promise.all([stop(server), stop(rebuildChild)]);
|
|
129
|
+
if (command === 'dev') await rm(devStateFile, { force: true });
|
|
130
|
+
};
|
|
131
|
+
process.once('SIGINT', shutdown);
|
|
132
|
+
process.once('SIGTERM', shutdown);
|
|
133
|
+
if (command !== 'dev') {
|
|
134
|
+
server = launch();
|
|
135
|
+
await done(server).catch(error => { if (!stopping) throw error; });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
let building = false;
|
|
139
|
+
let queued = false;
|
|
140
|
+
let attempted = false;
|
|
141
|
+
const {createDevChangeFilter} = await import('./runtime/dev-file-changes.mjs');
|
|
142
|
+
const filterChanges = createDevChangeFilter(root);
|
|
143
|
+
async function rebuild() {
|
|
144
|
+
if (stopping) return;
|
|
145
|
+
if (building) { queued = true; return; }
|
|
146
|
+
building = true;
|
|
147
|
+
const pending = [...changedFiles];
|
|
148
|
+
changedFiles.clear();
|
|
149
|
+
let buildOutput = '';
|
|
150
|
+
try {
|
|
151
|
+
const actualChanges = await filterChanges(pending);
|
|
152
|
+
if (attempted && !actualChanges.length) return;
|
|
153
|
+
attempted = true;
|
|
154
|
+
const changed = actualChanges.slice(0, 32).map(file => file.slice(0, 256));
|
|
155
|
+
await notifyDev({ state: 'building' });
|
|
156
|
+
if (actualChanges.some(file => /(?:^|\/)(?:(?:next|prnext|contentlayer)\.config\.|package(?:-lock)?\.json$|(?:pnpm-lock\.yaml|yarn\.lock)$)/.test(file))) { await stop(rebuildChild); rebuildChild = undefined; }
|
|
157
|
+
if (!rebuildChild || rebuildChild.exitCode !== null || rebuildChild.signalCode) {
|
|
158
|
+
rebuildChild = spawn(process.execPath, [path.join(path.dirname(self), 'build/dev-worker.mjs')], { stdio: ['inherit', 'pipe', 'pipe', 'ipc'], env: { ...process.env, NODE_ENV: process.env.NODE_ENV || 'development' } });
|
|
159
|
+
}
|
|
160
|
+
const stdout = chunk => { process.stdout.write(chunk); buildOutput = (buildOutput + chunk).slice(-12_000); };
|
|
161
|
+
const stderr = chunk => { process.stderr.write(chunk); buildOutput = (buildOutput + chunk).slice(-12_000); };
|
|
162
|
+
rebuildChild.stdout.on('data', stdout); rebuildChild.stderr.on('data', stderr);
|
|
163
|
+
let result;
|
|
164
|
+
try {
|
|
165
|
+
result = await new Promise((resolve, reject) => {
|
|
166
|
+
const worker = rebuildChild;
|
|
167
|
+
const cleanup = () => { worker.off('message', message); worker.off('exit', exited); worker.off('error', failed); };
|
|
168
|
+
const message = value => { if (value?.type === 'result') { cleanup(); resolve(value); } };
|
|
169
|
+
const exited = (code, signal) => { cleanup(); reject(new Error(`Compiler exited ${signal || code}`)); };
|
|
170
|
+
const failed = error => { cleanup(); reject(error); };
|
|
171
|
+
worker.on('message', message); worker.once('exit', exited); worker.once('error', failed);
|
|
172
|
+
worker.send({type:'build',root});
|
|
173
|
+
});
|
|
174
|
+
} finally { rebuildChild.stdout.off('data', stdout); rebuildChild.stderr.off('data', stderr); }
|
|
175
|
+
if (result.recycle) { await stop(rebuildChild); rebuildChild = undefined; }
|
|
176
|
+
if (!result.ok) throw new Error(result.error);
|
|
177
|
+
filterChanges.acceptInputs(result.inputs || []);
|
|
178
|
+
if (!stopping) {
|
|
179
|
+
await stop(server);
|
|
180
|
+
outputDirectory = await readBuildDirectory(root);
|
|
181
|
+
await watcher.refresh();
|
|
182
|
+
for (const file of changedFiles) {
|
|
183
|
+
if (file === outputDirectory || file.startsWith(outputDirectory + '/') || outputDirectory.startsWith(file + '/')) changedFiles.delete(file);
|
|
184
|
+
}
|
|
185
|
+
if (!changedFiles.size) { clearTimeout(timer); queued = false; }
|
|
186
|
+
server = launch();
|
|
187
|
+
server.on('error', error => console.error(`PRNext server: ${error.message}`));
|
|
188
|
+
const manifest = JSON.parse(await readFile(path.join(root, outputDirectory, 'manifest.json'), 'utf8'));
|
|
189
|
+
await notifyDev({ state: 'ready', buildId: manifest.buildId, clientManifest: manifest.devClient, changed });
|
|
190
|
+
console.log('Source compiled. React Fast Refresh applied in connected browsers.');
|
|
191
|
+
}
|
|
192
|
+
} catch (error) { if (!stopping) { console.error(`Build failed: ${error.message}`); await notifyDev({ state: 'error', error: buildOutput || error.message }); } }
|
|
193
|
+
finally { building = false; if (queued && !stopping) { queued = false; void rebuild(); } }
|
|
194
|
+
}
|
|
195
|
+
const { watchProject } = await import('./runtime/watch-project.mjs');
|
|
196
|
+
watcher = await watchProject(root, {
|
|
197
|
+
ignore: relative => relative === outputDirectory || relative.startsWith(outputDirectory + '/'),
|
|
198
|
+
onError: error => { console.error(`PRNext development watcher: ${error.message}`); process.exitCode = 1; void shutdown(); },
|
|
199
|
+
onChange: filename => {
|
|
200
|
+
const relative = String(filename || '').replaceAll(path.sep, '/');
|
|
201
|
+
if (relative === outputDirectory || relative.startsWith(outputDirectory + '/') || !shouldWatchProjectFile(filename)) return;
|
|
202
|
+
changedFiles.add(String(filename).replaceAll(path.sep, '/'));
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
timer = setTimeout(() => { if (changedFiles.size) void rebuild(); }, 150);
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
await rebuild();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
main().catch(error => { console.error(`PRNext: ${error.message}`); process.exitCode = 1; });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const { AppRouterContext } = require('./app-context.cjs');
|
|
6
|
+
|
|
7
|
+
function ClientPageRoot({ Component, params, searchParams, ...props }) {
|
|
8
|
+
const router = React.useContext(AppRouterContext);
|
|
9
|
+
const currentSearchParams = React.useMemo(() => {
|
|
10
|
+
if (!router) return searchParams;
|
|
11
|
+
let query;
|
|
12
|
+
function read() {
|
|
13
|
+
if (query) return query;
|
|
14
|
+
// Access belongs to the component that consumes the promise. Reading it
|
|
15
|
+
// in this framework wrapper would suspend every Client Page, even pages
|
|
16
|
+
// which never use searchParams, above their own Suspense boundaries.
|
|
17
|
+
const search = (router.pageSearchParams || router.searchParams).toString();
|
|
18
|
+
query = {};
|
|
19
|
+
for (const [name, value] of new URLSearchParams(search)) {
|
|
20
|
+
if (name === '_rsc') continue;
|
|
21
|
+
const next = Object.hasOwn(query, name) ? [...(Array.isArray(query[name]) ? query[name] : [query[name]]), value] : value;
|
|
22
|
+
Object.defineProperty(query, name, { value: next, enumerable: true, configurable: true, writable: true });
|
|
23
|
+
}
|
|
24
|
+
return query;
|
|
25
|
+
}
|
|
26
|
+
return { get status() { read(); return 'fulfilled'; }, get value() { return read(); },
|
|
27
|
+
then(resolve, reject) { try { resolve(read()); } catch (error) { if (reject) reject(error); else throw error; } } };
|
|
28
|
+
}, [router, searchParams]);
|
|
29
|
+
return React.createElement(Component, { ...props, params, searchParams: currentSearchParams });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports.ClientPageRoot = ClientPageRoot;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const { ReadonlyURLSearchParams } = require('./navigation.cjs');
|
|
6
|
+
const { ScriptContext } = require('./script-context.cjs');
|
|
7
|
+
const {AppRouterContext: NextAppRouterContext} = require('./next-app-router-context.cjs');
|
|
8
|
+
|
|
9
|
+
const AppRouterContext = React.createContext(null);
|
|
10
|
+
|
|
11
|
+
function unavailable() { throw new Error('App Router navigation is only available in the browser'); }
|
|
12
|
+
const serverRouter = Object.freeze({ push: unavailable, replace: unavailable, refresh: unavailable,
|
|
13
|
+
back: unavailable, forward: unavailable, prefetch: unavailable });
|
|
14
|
+
|
|
15
|
+
function AppRouterProvider({ router, controller, nonce, children, prerenderSearch, prerenderParams }) {
|
|
16
|
+
const pathname = router?.pathname || '/';
|
|
17
|
+
const basePath = router?.basePath || '';
|
|
18
|
+
const trailingSlash = router?.trailingSlash || false, skipTrailingSlashRedirect = router?.skipTrailingSlashRedirect || false;
|
|
19
|
+
const search = router?.forceStatic ? '' : router?.search || '';
|
|
20
|
+
const pageSearch = router?.forceStatic ? '' : router?.pageSearch ?? search;
|
|
21
|
+
const params = router?.params;
|
|
22
|
+
const value = React.useMemo(() => {
|
|
23
|
+
const searchParams = new ReadonlyURLSearchParams(search);
|
|
24
|
+
const pageSearchParams = new ReadonlyURLSearchParams(pageSearch);
|
|
25
|
+
return {
|
|
26
|
+
get pathname() { prerenderParams?.(); return pathname; }, basePath, trailingSlash, skipTrailingSlashRedirect,
|
|
27
|
+
get searchParams() { prerenderSearch?.(); return searchParams; },
|
|
28
|
+
get pageSearchParams() { prerenderSearch?.(); return pageSearchParams; },
|
|
29
|
+
get params() { prerenderParams?.(); return params || {}; },
|
|
30
|
+
router: controller || serverRouter,
|
|
31
|
+
};
|
|
32
|
+
}, [pathname, basePath, trailingSlash, skipTrailingSlashRedirect, search, pageSearch, params, controller, prerenderSearch, prerenderParams]);
|
|
33
|
+
const scripts = React.useMemo(() => ({ appDir: true, ssr: typeof window === 'undefined', nonce }), [nonce]);
|
|
34
|
+
return React.createElement(AppRouterContext.Provider, { value },
|
|
35
|
+
React.createElement(NextAppRouterContext.Provider,{value:value.router},React.createElement(ScriptContext.Provider, { value: scripts }, children)));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports.AppRouterContext = AppRouterContext;
|
|
39
|
+
module.exports.AppRouterProvider = AppRouterProvider;
|
|
40
|
+
module.exports.ReadonlyURLSearchParams = ReadonlyURLSearchParams;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Context, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { AppRouterInstance, ReadonlyURLSearchParams } from './navigation.cjs';
|
|
3
|
+
|
|
4
|
+
export interface AppRouterSnapshot {
|
|
5
|
+
pathname: string;
|
|
6
|
+
search?: string;
|
|
7
|
+
params?: Record<string, string | string[]>;
|
|
8
|
+
}
|
|
9
|
+
export const AppRouterContext: Context<{
|
|
10
|
+
pathname: string;
|
|
11
|
+
searchParams: ReadonlyURLSearchParams;
|
|
12
|
+
params: Record<string, string | string[]>;
|
|
13
|
+
router: AppRouterInstance;
|
|
14
|
+
} | null>;
|
|
15
|
+
export function AppRouterProvider(props: {
|
|
16
|
+
router: AppRouterSnapshot;
|
|
17
|
+
controller?: AppRouterInstance;
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
}): ReactElement;
|
|
20
|
+
export { ReadonlyURLSearchParams } from './navigation.cjs';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const React = require('react');
|
|
3
|
+
const { DynamicBailout } = require('./dynamic-bailout.cjs');
|
|
4
|
+
|
|
5
|
+
function moduleOf(value) {
|
|
6
|
+
return { default: value && 'default' in Object(value) ? value.default : value };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function ClientOnly({ children }) {
|
|
10
|
+
if (typeof window === 'undefined') throw new DynamicBailout();
|
|
11
|
+
return children;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function dynamic(input, options) {
|
|
15
|
+
// App Router deliberately accepts the function form here. Pages' direct
|
|
16
|
+
// Promise and first-argument options forms have a separate adapter.
|
|
17
|
+
const opts = { loader: () => Promise.resolve(() => null), loading: null, ssr: true,
|
|
18
|
+
...(typeof input === 'function' ? { loader: input } : {}), ...options };
|
|
19
|
+
if (!opts.ssr && typeof React.useEffect !== 'function') {
|
|
20
|
+
throw new Error('ssr: false is not supported with next/dynamic in Server Components. Move it into a Client Component.');
|
|
21
|
+
}
|
|
22
|
+
const Lazy = React.lazy(() => Promise.resolve().then(opts.loader).then(moduleOf));
|
|
23
|
+
function DynamicComponent(props) {
|
|
24
|
+
const fallback = opts.loading ? React.createElement(opts.loading, { isLoading: true, pastDelay: true, error: null }) : null;
|
|
25
|
+
const child = React.createElement(Lazy, props);
|
|
26
|
+
if (!opts.ssr) return React.createElement(React.Suspense, { fallback }, React.createElement(ClientOnly, null, child));
|
|
27
|
+
return opts.loading ? React.createElement(React.Suspense, { fallback }, child) : child;
|
|
28
|
+
}
|
|
29
|
+
DynamicComponent.displayName = 'LoadableComponent';
|
|
30
|
+
return DynamicComponent;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = dynamic;
|
|
34
|
+
module.exports.default = dynamic;
|