This repository has been archived by the owner on Aug 5, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #196 from TrueCar/toddw/multiple-entry-points
Add support for multiple entrypoints
- Loading branch information
Showing
19 changed files
with
645 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,7 @@ | ||
/*global __PATH_TO_ENTRY__*/ | ||
/*global __webpack_public_path__*/ | ||
/*exported __webpack_public_path__*/ | ||
require("match-media/matchMedia.js"); | ||
require("match-media/matchMedia.addListener.js"); | ||
require("babel-polyfill"); | ||
__webpack_public_path__ = window.__GS_PUBLIC_PATH__; | ||
const Entry = require(__PATH_TO_ENTRY__).default; | ||
Entry.start(); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import getWebpackAdditions from "./getWebpackAdditions"; | ||
|
||
function getBasePath () { | ||
return path.join(process.cwd(), "src", "config", ".entries"); | ||
} | ||
|
||
/** | ||
* This method is a information preparer/gatherer. This information is used by | ||
* `buildWebpackEntries` and the server request handler. It sets up the default | ||
* entry point that will be used by most apps, as well as combining any | ||
* additional entry points specified in the webpack additions. | ||
*/ | ||
export function getWebpackEntries () { | ||
const output = {}; | ||
const cwd = process.cwd(); | ||
const basePath = getBasePath(); | ||
|
||
// setup default | ||
const entryPoints = { | ||
"/": { | ||
name: "main", | ||
routes: path.join(cwd, "src", "config", "routes"), | ||
reducers: path.join(cwd, "src", "reducers") | ||
}, | ||
...getWebpackAdditions().entryPoints | ||
}; | ||
|
||
Object.keys(entryPoints).forEach((key) => { | ||
const entry = entryPoints[key]; | ||
const fileName = entry.name.replace(/\W/, "-"); | ||
output[key] = { | ||
...entry, | ||
fileName: fileName, | ||
filePath: `${path.join(basePath, fileName)}.js`, | ||
routes: entry.routes || path.join(cwd, "src", "config", "routes", fileName), | ||
reducers: entry.reducers || path.join(cwd, "src", "reducers", fileName), | ||
index: entry.index || path.join(cwd, "Index") | ||
}; | ||
}); | ||
|
||
return output; | ||
} | ||
|
||
|
||
/** | ||
* This method will wipe out the `config/.entries` hidden folder and rebuild it | ||
* it based on the defined entry points. It creates entry point files for each | ||
* of the entry points and returns the hash that webpack uses when bundling | ||
* code. | ||
* | ||
* Each entry point will be an array including the shared client entry point | ||
* file which includes global dependencies like the babel polyfill. It then | ||
* includes the generated entry point. Finally, if we are in development mode | ||
* it will start the array off with the webpack hot middleware client. | ||
*/ | ||
export default function buildWebpackEntries (isProduction) { | ||
const output = {}; | ||
const basePath = getBasePath(); | ||
|
||
// Clean slate | ||
fs.removeSync(basePath); | ||
fs.ensureDirSync(basePath); | ||
|
||
const entries = getWebpackEntries(); | ||
for (const key in entries) { | ||
const entry = entries[key]; | ||
const { filePath, fileName } = entry; | ||
fs.outputFileSync(filePath, getEntryPointContent(entry)); | ||
output[fileName] = [path.join(__dirname, "..", "entrypoints", "client.js"), filePath]; | ||
|
||
// Include hot middleware in development mode only | ||
if (!isProduction) { | ||
output[fileName].unshift("webpack-hot-middleware/client"); | ||
} | ||
} | ||
|
||
return output; | ||
} | ||
|
||
function getEntryPointContent (entry) { | ||
const cwd = process.cwd(); | ||
const { routes, index, reducers } = entry; | ||
const reduxMiddlewarePath = path.join(cwd, "src", "config", "redux-middleware"); | ||
const config = path.join(cwd, "src", "config", "application"); | ||
const mainEntry = path.join(cwd, "src", "config", ".entry"); | ||
const output = `import getRoutes from "${routes}"; | ||
// Make sure that webpack considers new dependencies introduced in the Index | ||
// file | ||
import "${index}"; | ||
import config from "${config}"; | ||
import Entry from "${mainEntry}"; | ||
import { createStore } from "gluestick-shared"; | ||
import middleware from "${reduxMiddlewarePath}"; | ||
export function getStore (httpClient) { | ||
const store = createStore(httpClient, () => require("${reducers}"), middleware, (cb) => module.hot && module.hot.accept("${reducers}", cb), !!module.hot); | ||
return store; | ||
} | ||
if (typeof window === "object") { | ||
Entry.start(getRoutes, getStore); | ||
} | ||
`; | ||
|
||
return output; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { relative } from "path"; | ||
|
||
export default function isChildPath (parent, child) { | ||
if (parent === "/") { | ||
return true; | ||
} | ||
|
||
return relative(parent, child).substr(0, 1) !== "."; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { parse as parseURL } from "url"; | ||
import { getWebpackEntries } from "../buildWebpackEntries"; | ||
import isChildPath from "../isChildPath"; | ||
import { getHttpClient } from "gluestick-shared"; | ||
|
||
/** | ||
* This method takes the server request object, determines which entry point | ||
* the server should use for rendering and then prepares the necessary | ||
* variables that the server needs to render. These variables include Index, | ||
* store, getRoutes and fileName. | ||
*/ | ||
export default function getRenderRequirementsFromEntrypoints (req, config={}, customRequire=require) { | ||
const httpClient = getHttpClient(config.httpClient, req); | ||
const entryPoints = getWebpackEntries(); | ||
|
||
/** | ||
* Sort through all of the entry points based on the number of `/` characters | ||
* found in the url. It will test the most deeply nested entry points first | ||
* while finally falling back to the default index parameter. | ||
*/ | ||
const sortedEntries = Object.keys(entryPoints).sort((a, b) => { | ||
const bSplitLength = b.split("/").length; | ||
const aSplitLength = a.split("/").length; | ||
if (bSplitLength === aSplitLength) { | ||
return b.length - a.length; | ||
} | ||
|
||
return bSplitLength - aSplitLength; | ||
}); | ||
|
||
const { path: urlPath } = parseURL(req.url); | ||
|
||
/** | ||
* Loop through the sorted entry points and return the variables that the | ||
* server needs to render based on the best matching entry point. | ||
*/ | ||
for (const path of sortedEntries) { | ||
if (isChildPath(path, urlPath)) { | ||
const { routes, index, fileName, filePath } = entryPoints[path]; | ||
return { | ||
Index: customRequire(index + ".js").default, | ||
store: customRequire(filePath).getStore(httpClient), | ||
getRoutes: customRequire(routes).default, | ||
fileName | ||
}; | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.