mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Parcel build
This commit is contained in:
parent
02acd1b608
commit
3640738f36
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,3 +19,4 @@ coverage
|
|||
# test output
|
||||
test/**/out
|
||||
.DS_Store
|
||||
.cache
|
||||
|
|
299
packages/next-build/index.js
Normal file
299
packages/next-build/index.js
Normal file
|
@ -0,0 +1,299 @@
|
|||
const JSPackager = require('parcel/src/packagers/JSPackager')
|
||||
const {glob} = require('parcel/src/utils/glob')
|
||||
const urlJoin = require('parcel/src/utils/urlJoin');
|
||||
const path = require('path');
|
||||
const {promisify} = require('util')
|
||||
const fs = require('fs')
|
||||
const loadConfig = require('next-server/next-config').default
|
||||
const { PHASE_PRODUCTION_BUILD, BUILD_ID_FILE } = require('next-server/constants')
|
||||
const addMDXtoBundler = require('@mdx-js/parcel-plugin-mdx')
|
||||
const access = promisify(fs.access)
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
|
||||
const PAGES_ROUTE_NAME_REGEX = /^pages[/\\](.*)\.js$/
|
||||
const DEFAULT_ROUTE_REGEX = /[/\\]next[/\\]dist[/\\]pages[/\\](.*)\.js$/
|
||||
const ROUTE_NAME_REGEX = /static[/\\][^/\\]+[/\\]pages[/\\](.*)\.js$/
|
||||
const ROUTE_NAME_SERVER_REGEX = /[/\\]server[/\\]pages[/\\](.*)\.js$/
|
||||
const NEXT_RUNTIME_REGEX = /[/\\]next[/\\]dist[/\\]client[/\\]next/
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
async function writeBuildId (distDir, buildId) {
|
||||
const buildIdPath = path.join(distDir, BUILD_ID_FILE)
|
||||
await writeFile(buildIdPath, buildId, 'utf8')
|
||||
}
|
||||
|
||||
function normalizePageName(page) {
|
||||
if(page === 'index') {
|
||||
return page
|
||||
}
|
||||
|
||||
return page.replace(/(^|\/)index$/, '')
|
||||
}
|
||||
|
||||
function createServerPackager() {
|
||||
return class NextServerPackager extends JSPackager {
|
||||
getBundleSpecifier(bundle) {
|
||||
let name = path.relative(path.dirname(this.bundle.name), bundle.name);
|
||||
if (bundle.entryAsset) {
|
||||
return [name, bundle.entryAsset.id];
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
async end() {
|
||||
let entry = [];
|
||||
|
||||
// Add the HMR runtime if needed.
|
||||
if (this.options.hmr) {
|
||||
let asset = await this.bundler.getAsset(
|
||||
require.resolve('../builtins/hmr-runtime')
|
||||
);
|
||||
await this.addAssetToBundle(asset);
|
||||
entry.push(asset.id);
|
||||
}
|
||||
|
||||
if (await this.writeBundleLoaders()) {
|
||||
entry.push(0);
|
||||
}
|
||||
|
||||
if (this.bundle.entryAsset && this.externalModules.size === 0) {
|
||||
entry.push(this.bundle.entryAsset.id);
|
||||
}
|
||||
|
||||
await this.dest.write(
|
||||
`},(typeof window !== 'undefined' ? (global.__NEXT_CACHE__ = global.__NEXT_CACHE__ || {}) : {}),` +
|
||||
JSON.stringify(entry) +
|
||||
', ' +
|
||||
JSON.stringify(this.options.global || null) +
|
||||
')'
|
||||
);
|
||||
|
||||
if (this.options.sourceMaps) {
|
||||
// Add source map url if a map bundle exists
|
||||
let mapBundle = this.bundle.siblingBundlesMap.get('map');
|
||||
if (mapBundle) {
|
||||
let mapUrl = urlJoin(
|
||||
this.options.publicURL,
|
||||
path.basename(mapBundle.name)
|
||||
);
|
||||
await this.write(`\n//# sourceMappingURL=${mapUrl}`);
|
||||
}
|
||||
}
|
||||
await this.dest.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createClientPackager({buildId}) {
|
||||
return class NextClientPackager extends JSPackager {
|
||||
getBundleSpecifier(bundle) {
|
||||
let name = path.relative(path.dirname(this.bundle.name), bundle.name);
|
||||
if (bundle.entryAsset) {
|
||||
return [name, bundle.entryAsset.id];
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
async start() {
|
||||
const result = ROUTE_NAME_REGEX.exec(this.bundle.name) || DEFAULT_ROUTE_REGEX.exec(this.bundle.name)
|
||||
if(result) {
|
||||
this.isPageBundle = true
|
||||
let routeName = result ? result[1] : defaultPage[1]
|
||||
// We need to convert \ into / when we are in windows
|
||||
// to get the proper route name
|
||||
// Here we need to do windows check because it's possible
|
||||
// to have "\" in the filename in unix.
|
||||
// Anyway if someone did that, he'll be having issues here.
|
||||
// But that's something we cannot avoid.
|
||||
if (/^win/.test(process.platform)) {
|
||||
routeName = routeName.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
routeName = `/${routeName.replace(/(^|\/)index$/, '')}`
|
||||
await this.write(`__NEXT_REGISTER_PAGE("${routeName}", function() {var module={};var exports={};`)
|
||||
}
|
||||
await super.start()
|
||||
}
|
||||
|
||||
async end() {
|
||||
let entry = [];
|
||||
|
||||
// Add the HMR runtime if needed.
|
||||
if (this.options.hmr) {
|
||||
let asset = await this.bundler.getAsset(
|
||||
require.resolve('../builtins/hmr-runtime')
|
||||
);
|
||||
await this.addAssetToBundle(asset);
|
||||
entry.push(asset.id);
|
||||
}
|
||||
|
||||
if (await this.writeBundleLoaders()) {
|
||||
entry.push(0);
|
||||
}
|
||||
|
||||
if (this.bundle.entryAsset && this.externalModules.size === 0) {
|
||||
entry.push(this.bundle.entryAsset.id);
|
||||
}
|
||||
|
||||
await this.dest.write(
|
||||
`},(typeof window !== 'undefined' ? (window.__NEXT_CACHE__ = window.__NEXT_CACHE__ || {}) : {}),` +
|
||||
JSON.stringify(entry) +
|
||||
', ' +
|
||||
JSON.stringify(this.options.global || null) +
|
||||
')'
|
||||
);
|
||||
|
||||
if(this.isPageBundle) {
|
||||
await this.dest.write(
|
||||
';return {page: module.exports.default}})'
|
||||
)
|
||||
}
|
||||
|
||||
if (this.options.sourceMaps) {
|
||||
// Add source map url if a map bundle exists
|
||||
let mapBundle = this.bundle.siblingBundlesMap.get('map');
|
||||
if (mapBundle) {
|
||||
let mapUrl = urlJoin(
|
||||
this.options.publicURL,
|
||||
path.basename(mapBundle.name)
|
||||
);
|
||||
await this.write(`\n//# sourceMappingURL=${mapUrl}`);
|
||||
}
|
||||
}
|
||||
await this.dest.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPageFiles({dir, config, isClient}) {
|
||||
const result = glob.sync(`pages/**/${isClient ? '!(_document)' : ''}*.+(${config.pageExtensions.join('|')})`, { cwd: dir, absolute: true })
|
||||
const appPath = path.join(dir, 'pages', '_app.js')
|
||||
if(!result.some((item) => item === appPath)) {
|
||||
result.push(require.resolve('next/dist/pages/_app.js'))
|
||||
}
|
||||
|
||||
const errorPath = path.join(dir, 'pages', '_error.js')
|
||||
if(!result.some((item) => item === errorPath)) {
|
||||
result.push(require.resolve('next/dist/pages/_error.js'))
|
||||
}
|
||||
|
||||
if(!isClient) {
|
||||
const documentPath = path.join(dir, 'pages', '_document.js')
|
||||
if(!result.some((item) => item === documentPath)) {
|
||||
result.push(require.resolve('next/dist/pages/_document.js'))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function clientBundler({Bundler, dir, buildId, config}) {
|
||||
const clientPages = getPageFiles({dir, config, isClient: true})
|
||||
const entryFiles = [
|
||||
require.resolve('next/dist/client/next.js'),
|
||||
...clientPages
|
||||
];
|
||||
|
||||
const options = {
|
||||
outDir: path.join(dir, '.next', 'static'),
|
||||
watch: false,
|
||||
sourceMaps: false,
|
||||
minify: true,
|
||||
target: 'browser'
|
||||
}
|
||||
|
||||
// Initializes a bundler using the entrypoint location and options provided
|
||||
const bundler = new Bundler(entryFiles, options);
|
||||
|
||||
// Make sure pages get resolved from the root dir
|
||||
bundler.options.rootDir = dir
|
||||
|
||||
bundler.addPackager('js', createClientPackager({buildId}))
|
||||
bundler.addAssetType('js', path.join(__dirname, 'nextjsasset.js'))
|
||||
bundler.addAssetType('jsx', path.join(__dirname, 'nextjsasset.js'))
|
||||
addMDXtoBundler(bundler)
|
||||
|
||||
// Run the bundler, this returns the main bundle
|
||||
// Use the events if you're using watch mode as this promise will only trigger once and not for every rebuild
|
||||
const bundle = await bundler.bundle();
|
||||
}
|
||||
|
||||
async function serverBundler({Bundler, dir, buildId, config}) {
|
||||
const serverPages = getPageFiles({dir, config})
|
||||
const entryFiles = serverPages
|
||||
|
||||
const options = {
|
||||
outDir: path.join(dir, '.next', 'server'),
|
||||
watch: false,
|
||||
sourceMaps: false,
|
||||
minify: true,
|
||||
target: 'node'
|
||||
}
|
||||
|
||||
// Initializes a bundler using the entrypoint location and options provided
|
||||
const bundler = new Bundler(entryFiles, options);
|
||||
|
||||
// Make sure pages get resolved from the root dir
|
||||
bundler.options.rootDir = dir
|
||||
|
||||
bundler.addPackager('js', createServerPackager())
|
||||
bundler.addAssetType('js', path.join(__dirname, 'nextjsasset.js'))
|
||||
bundler.addAssetType('jsx', path.join(__dirname, 'nextjsasset.js'))
|
||||
addMDXtoBundler(bundler)
|
||||
|
||||
// Run the bundler, this returns the main bundle
|
||||
// Use the events if you're using watch mode as this promise will only trigger once and not for every rebuild
|
||||
const bundle = await bundler.bundle();
|
||||
}
|
||||
|
||||
function rewriteFileName(Bundle, buildId) {
|
||||
const nextClientRuntime = require.resolve('next/dist/client/next.js')
|
||||
const originalGetHashedBundleName = Bundle.prototype.getHashedBundleName
|
||||
Bundle.prototype.getHashedBundleName = function getHashedBundleName(contentHash) {
|
||||
const entryAsset = this.entryAsset || this.parentBundle.entryAsset;
|
||||
const isEntry = entryAsset.options.entryFiles.includes(entryAsset.name) || Array.from(entryAsset.parentDeps).some(dep => dep.entry);
|
||||
const originalResult = originalGetHashedBundleName.call(this, contentHash)
|
||||
if(!isEntry) {
|
||||
return originalResult
|
||||
}
|
||||
|
||||
if(entryAsset.name === nextClientRuntime) {
|
||||
return path.join(buildId, 'main.js')
|
||||
}
|
||||
|
||||
const result = PAGES_ROUTE_NAME_REGEX.exec(originalResult) || DEFAULT_ROUTE_REGEX.exec(originalResult)
|
||||
if(!result) {
|
||||
return originalResult
|
||||
}
|
||||
|
||||
const page = normalizePageName(result[1])
|
||||
const ext = path.extname(originalResult)
|
||||
const normalizedPagePath = path.join(buildId, 'pages', page + ext)
|
||||
return normalizedPagePath
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = async function build({dir, conf}) {
|
||||
const config = loadConfig(PHASE_PRODUCTION_BUILD, dir, conf)
|
||||
const buildId = await config.generateBuildId().trim() // defaults to a uuid
|
||||
const distDir = path.join(dir, config.distDir)
|
||||
|
||||
try {
|
||||
await access(dir, (fs.constants || fs).W_OK)
|
||||
} catch (err) {
|
||||
console.error(`> Failed, build directory is not writeable. https://err.sh/zeit/next.js/build-dir-not-writeable`)
|
||||
throw err
|
||||
}
|
||||
|
||||
const Bundle = require('parcel/src/Bundle')
|
||||
rewriteFileName(Bundle, buildId)
|
||||
|
||||
const Bundler = require('parcel');
|
||||
|
||||
await serverBundler({Bundler, dir, buildId, config})
|
||||
await clientBundler({Bundler, dir, buildId, config})
|
||||
await writeBuildId(distDir, buildId)
|
||||
}
|
44
packages/next-build/nextjsasset.js
Normal file
44
packages/next-build/nextjsasset.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
const JSAsset = require('parcel/src/assets/JSAsset');
|
||||
const envVisitor = require('parcel/src/visitors/env');
|
||||
const ENV_RE = /\b(?:process\.env)\b/;
|
||||
|
||||
const babel7 = require('parcel/src/transforms/babel/babel7');
|
||||
const getBabelConfig = require('parcel/src/transforms/babel/config');
|
||||
|
||||
async function babelTransform(asset) {
|
||||
// let config = await getBabelConfig(asset);
|
||||
// console.log(config[7])
|
||||
// if (config[6]) {
|
||||
// await babel6(asset, config[6]);
|
||||
// }
|
||||
|
||||
// if (config[7]) {
|
||||
await babel7(asset, {
|
||||
babelVersion: 7,
|
||||
config: {
|
||||
presets: ['next/babel']
|
||||
}
|
||||
});
|
||||
// }
|
||||
|
||||
return asset.ast;
|
||||
}
|
||||
|
||||
|
||||
class NextJSAsset extends JSAsset {
|
||||
async pretransform() {
|
||||
if (this.options.sourceMaps) {
|
||||
await this.loadSourceMap();
|
||||
}
|
||||
|
||||
await babelTransform(this);
|
||||
|
||||
// Inline environment variables
|
||||
if (this.options.target === 'browser' && ENV_RE.test(this.contents)) {
|
||||
await this.parseIfNeeded();
|
||||
this.traverseFast(envVisitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NextJSAsset
|
11
packages/next-build/package.json
Normal file
11
packages/next-build/package.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "next-build",
|
||||
"version": "7.0.2-canary.8",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@mdx-js/parcel-plugin-mdx": "0.15.5",
|
||||
"@mdx-js/tag": "0.15.0",
|
||||
"next-server": "^7.0.2-canary.8",
|
||||
"parcel": "1.10.3"
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ async function doRender (req, res, pathname, query, {
|
|||
await hotReloader.ensurePage(page)
|
||||
}
|
||||
|
||||
const pagePath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', normalizedPagePath)
|
||||
const pagePath = join(distDir, SERVER_DIRECTORY, /* CLIENT_STATIC_FILES_PATH, */ buildId, 'pages', normalizedPagePath)
|
||||
|
||||
try {
|
||||
await access(`${pagePath}.js`, (fs.constants || fs).R_OK)
|
||||
|
@ -84,8 +84,8 @@ async function doRender (req, res, pathname, query, {
|
|||
throw pageNotFoundError(page)
|
||||
}
|
||||
|
||||
const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document')
|
||||
const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app')
|
||||
const documentPath = join(distDir, SERVER_DIRECTORY, /* CLIENT_STATIC_FILES_PATH, */ buildId, 'pages', '_document')
|
||||
const appPath = join(distDir, SERVER_DIRECTORY, /* CLIENT_STATIC_FILES_PATH, */ buildId, 'pages', '_app')
|
||||
|
||||
let Document = require(documentPath)
|
||||
let App = require(appPath)
|
||||
|
@ -95,8 +95,8 @@ async function doRender (req, res, pathname, query, {
|
|||
App = App.default || App
|
||||
Component = Component.default || Component
|
||||
|
||||
const reactLoadableManifest = require(pagePath + '-loadable.json')
|
||||
const buildManifest = require(pagePath + '-assets.json')
|
||||
// const reactLoadableManifest = require(pagePath + '-loadable.json')
|
||||
// const buildManifest = require(pagePath + '-assets.json')
|
||||
|
||||
if (typeof Component !== 'function') {
|
||||
throw new Error(`The default export is not a React Component in page: "${page}"`)
|
||||
|
@ -106,7 +106,8 @@ async function doRender (req, res, pathname, query, {
|
|||
const ctx = { err, req, res, pathname, query, asPath }
|
||||
const router = new Router(pathname, query, asPath)
|
||||
const props = await loadGetInitialProps(App, {Component, router, ctx})
|
||||
const files = buildManifest.assets
|
||||
// const files = buildManifest.assets
|
||||
const files = [`static/${buildId}/main.js`]
|
||||
|
||||
// the response might be finshed on the getinitialprops call
|
||||
if (isResSent(res)) return
|
||||
|
@ -153,13 +154,14 @@ async function doRender (req, res, pathname, query, {
|
|||
head = Head.rewind() || defaultHead()
|
||||
}
|
||||
|
||||
return { html, head, buildManifest }
|
||||
return { html, head, /* buildManifest */ }
|
||||
}
|
||||
|
||||
await Loadable.preloadAll() // Make sure all dynamic imports are loaded
|
||||
|
||||
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
|
||||
const dynamicImports = [...(new Set(getDynamicImportBundles(reactLoadableManifest, reactLoadableModules)))]
|
||||
// const dynamicImports = [...(new Set(getDynamicImportBundles(reactLoadableManifest, reactLoadableModules)))]
|
||||
const dynamicImports = []
|
||||
const dynamicImportsIds = dynamicImports.map((bundle) => bundle.id)
|
||||
|
||||
if (isResSent(res)) return
|
||||
|
@ -181,7 +183,7 @@ async function doRender (req, res, pathname, query, {
|
|||
dev,
|
||||
dir,
|
||||
staticMarkup,
|
||||
buildManifest,
|
||||
// buildManifest,
|
||||
files,
|
||||
dynamicImports,
|
||||
assetPrefix, // We always pass assetPrefix as a top level property since _document needs it to render, even though the client side might not need it
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
import { resolve, join } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
import parseArgs from 'minimist'
|
||||
import build from '../build'
|
||||
// import build from '../build'
|
||||
import build from 'next-build'
|
||||
import { printAndExit } from '../server/lib/utils'
|
||||
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
|
|
|
@ -46,7 +46,7 @@ module.exports = (context, opts = {}) => ({
|
|||
plugins: [
|
||||
require('babel-plugin-react-require'),
|
||||
require('@babel/plugin-syntax-dynamic-import'),
|
||||
require('./plugins/react-loadable-plugin'),
|
||||
// require('./plugins/react-loadable-plugin'),
|
||||
require('./plugins/next-to-next-server'),
|
||||
[require('@babel/plugin-proposal-class-properties'), opts['class-properties'] || {}],
|
||||
require('@babel/plugin-proposal-object-rest-spread'),
|
||||
|
|
|
@ -37,7 +37,7 @@ const prefix = assetPrefix || ''
|
|||
|
||||
// With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time
|
||||
// So, this is how we do it in the client side at runtime
|
||||
__webpack_public_path__ = `${prefix}/_next/` //eslint-disable-line
|
||||
// __webpack_public_path__ = `${prefix}/_next/` //eslint-disable-line
|
||||
// Initialize next/asset with the assetPrefix
|
||||
asset.setAssetPrefix(prefix)
|
||||
// Initialize next/config with the environment configuration
|
||||
|
@ -48,7 +48,7 @@ envConfig.setConfig({
|
|||
|
||||
const asPath = getURL()
|
||||
|
||||
const pageLoader = new PageLoader(buildId, prefix)
|
||||
const pageLoader = new PageLoader({buildId, assetPrefix: prefix})
|
||||
window.__NEXT_LOADED_PAGES__.forEach(([r, f]) => {
|
||||
pageLoader.registerPage(r, f)
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ import EventEmitter from 'next-server/dist/lib/EventEmitter'
|
|||
const webpackModule = module
|
||||
|
||||
export default class PageLoader {
|
||||
constructor (buildId, assetPrefix) {
|
||||
constructor ({buildId, assetPrefix}) {
|
||||
this.buildId = buildId
|
||||
this.assetPrefix = assetPrefix
|
||||
|
||||
|
|
Loading…
Reference in a new issue