diff --git a/packages/electrode-react-webapp/.babelrc b/packages/electrode-react-webapp/.babelrc deleted file mode 100644 index 24b086f24..000000000 --- a/packages/electrode-react-webapp/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "8" - } - } - ], - "@babel/preset-react" - ] -} diff --git a/packages/electrode-react-webapp/.eslintignore b/packages/electrode-react-webapp/.eslintignore deleted file mode 100644 index c528e9065..000000000 --- a/packages/electrode-react-webapp/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -coverage -resources -node_modules -lib/templates -test/data \ No newline at end of file diff --git a/packages/electrode-react-webapp/.eslintrc b/packages/electrode-react-webapp/.eslintrc deleted file mode 100644 index 5f6ea6525..000000000 --- a/packages/electrode-react-webapp/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ ---- -extends: - - "./node_modules/electrode-archetype-njs-module-dev/config/eslint/.eslintrc-node" diff --git a/packages/electrode-react-webapp/.gitignore b/packages/electrode-react-webapp/.gitignore deleted file mode 100644 index 3236cba6d..000000000 --- a/packages/electrode-react-webapp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -template diff --git a/packages/electrode-react-webapp/CHANGELOG.json b/packages/electrode-react-webapp/CHANGELOG.json deleted file mode 100644 index 547a073c6..000000000 --- a/packages/electrode-react-webapp/CHANGELOG.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "electrode-react-webapp", - "entries": [ - { - "version": "5.1.1", - "tag": "electrode-react-webapp_v5.1.1", - "date": "Tue, 29 Aug 2023 14:57:37 GMT", - "comments": { - "patch": [ - { - "comment": "Update dev dependencies" - } - ] - } - }, - { - "version": "5.1.0", - "tag": "electrode-react-webapp_v5.1.0", - "date": "Thu, 04 May 2023 21:08:31 GMT", - "comments": { - "minor": [ - { - "comment": "`bable/*` minor version upgrades" - } - ] - } - } - ] -} diff --git a/packages/electrode-react-webapp/CHANGELOG.md b/packages/electrode-react-webapp/CHANGELOG.md deleted file mode 100644 index 8a4c35aa5..000000000 --- a/packages/electrode-react-webapp/CHANGELOG.md +++ /dev/null @@ -1,18 +0,0 @@ -# Change Log - electrode-react-webapp - -This log was last generated on Tue, 29 Aug 2023 14:57:37 GMT and should not be manually modified. - -## 5.1.1 -Tue, 29 Aug 2023 14:57:37 GMT - -### Patches - -- Update dev dependencies - -## 5.1.0 -Thu, 04 May 2023 21:08:31 GMT - -### Minor changes - -- `bable/*` minor version upgrades - diff --git a/packages/electrode-react-webapp/DESIGN.md b/packages/electrode-react-webapp/DESIGN.md deleted file mode 100644 index 8ac7d106a..000000000 --- a/packages/electrode-react-webapp/DESIGN.md +++ /dev/null @@ -1,330 +0,0 @@ -# Design of the index.html rendering flow - -## Table of Contents - -* [Considerations](#considerations) -* [Tokens](#tokens) - + [Token Example](#token-example) - + [Invoking token handler/process functions](#invoking-token-handlerprocess-functions) - + [Token Props](#token-props) - + [Internal Props](#internal-props) -* [Predefined Tokens and Handler](#predefined-tokens-and-handler) -* [Custom Processing](#custom-processing) - + [Handler](#handler) - + [Custom Processor Module](#custom-processor-module) - + [The Process Function](#the-process-function) - + [`context`](#context) - + [Concurrent Token and Async Output](#concurrent-token-and-async-output) - + [routeData](#routedata) - -## Considerations - -The design of the rendering flow took the following into consideration. - -- A simple token base template rendering - -- The document output to be a stream and allow any token to trigger a flush at any time. - -- Allow tokens to be async so it can call services etc to produce the data. - -- Allow token proccessor to open a spot in the output for update later. A flush will wait for open spots to close. This enables concurrently processing tokens that can send text to fixed spots in the output doc. - -## Tokens - -**Token is the basic building block on how your `index` page will be built. Following are details about how they work and the types of tokens.** - -- **Syntax** Within your template file, special tokens can be specified with ``, or `/*--%{token}--*/` - - - Where `token` is the string referring to a token or name of a processor module. - - - Tokens can also be multi lines. - - - **Comments** can be added as lines that start with `//`. - - - must be in their own lines only. - - - **Props** Just like HTML tags, [props](#token-props) can be specified for a token. - -- **Token Module**: To specify a processor module, start the token with `#`. ie: ``, where `module_name` specifies a name for a [Custom Processor Module](#custom-processor-module). - - - The module will be loaded with `require`. If the `module_name` starts with `.`, then the module is required from where the template file is. For example, `` will load the module `./lib/custom` relative to the dir the template is located. - -### Token Example - -```html - - - - -
blah
- -``` - -### Invoking token handler/process functions - -The **token** has either a handler function or a module with a process function. - -The function are invoked with the token object instance as `this`. - -So for example, to access the [Token props](#token-props) you can access `this.props` which is an object with props specified in the template. - -### Token Props - -- You can specify Key Value Pair props for your tokens in the form of `name=value` - - - ie: `` - - - String prop values must be enclosed in `"` or `'`. - - - Values started with `[` will be parsed with [string-array](https://www.npmjs.com/package/string-array) - - - Any other values will be parsed with `JSON.parse`, so only `false`, `true`, numbers, `null`, or a stringified JSON that has absolutely no spaces anywhere. - -- If you want, you can specify a single JSON object following the token to act as the props object. - -For example: - -```html - -``` - -### Internal Props - -Some props with leading `_` may be used to control the template engine. These are supported: - -- `_call="function_name"` - Tells the template engine what function from a token module to call. - - - Expect your token loads a custom processor module that instead of exporting a setup funtion, exports an object with setup functions. - - The `_call` prop specifies the name of a function from that module to call. - -For example: - -```html - -``` - -You can also use a [string-array](https://www.npmjs.com/package/string-array) to have additional strings params passed to the function. - -For example: - -```html - -``` - -This will call the function `setup1(options, tokenInstance, a, b)` from your module like this: - -```js -tokenModule.setup1(options, tokenObj, "param1", "param2"); -``` - -Where `tokenObj` is the your token's instance object. - -Your token's `props` object is accesible through `tokenObj.props`. - -## Predefined Tokens and Handler - -These are tokens that are automatically processed internally: - -- `INITIALIZE` -- `META_TAGS` -- `PAGE_TITLE` -- `CRITICAL_CSS` -- `WEBAPP_HEADER_BUNDLES` -- `HEAD_CLOSED` -- `SSR_CONTENT` -- `AFTER_SSR_CONTENT` -- `PREFETCH_BUNDLES` -- `WEBAPP_BODY_BUNDLES` -- `BODY_CLOSED` -- `HTML_CLOSED` - -A builtin token handler is used to process these. - -If you've registered your own [handler](#handler) for any of them, you can still access the builtin token handler as `context.user.routeData.tokenHandler`. - -## Custom Processing - -There are two ways to provide your own processor for tokens. - -1. Provide a handler that can process tokens -2. Provide a custom processor module - -### Handler - -You can register a token handler for each route. The token handler should export a function `setup` that returns an object with the names of your custom tokens each associated with its own handler function. - -For example, you can replace the predefined `SSR_CONTENT` token with your own handler, and define a new token `MY_NEW_TOKEN`. - -```js -module.exports = function setup(options, tokenObj) { - return { - SSR_CONTENT: (context, [next]) => {}, - MY_NEW_TOKEN: context => `hello from MY_NEW_TOKEN` - }; -}; -``` - -- `options` will contain the following: - - - `routeOptions` - original options passed in - - `routeData` - global data for the route - -- `tokenObj` is the instance object for your token. - - - Your token's `props` object is accesible through `tokenObj.props`. - -### Custom Processor Module - -- The custom processor module should export an initialize function as below: - -```js -module.exports = function setup(options, tokenObj) { - return { - process: function(context, [next]) {} - }; -}; -``` - -- You can also export an object with multiple setup functions and use the `_call` prop to call specific ones: - -```js -function setup1(options, tokenObj) { - return { - process: function(context, [next]) {} - }; -} - -function setup2(options, tokenObj) { - return { - process: function(context, [next]) {} - }; -} - -module.exports = { - setup1, - setup2 -}; -``` - -And you can load the same module but call different setup function with the `_call` prop. - -```html -Calling setup1 of lib/my-handlers.js - - -Calling setup2 of lib/my-handlers.js - -``` - -### The Process Function - -As shown above, the token handler or custom process function should have the following signature: - -```js -function (context, [next]) {} -``` - -> Note that the setup is only called once for each route. The instance returned is reused for every request. So be sure to avoid any global state. Everything should operate on the `context` passed in. - -Parameters: - -- If `next` is specified then it's async and rendering will only continue when it's called. - -- The function can return a Promise to be async if it doesn't want to take a next callback. - -- If function returns a string it will be added to the output stream. - -- Other return values are ignored. - -### `context` - -`context` will contain an `output` object with the following methods: - -- `add(string)` - Adds `string` to the output stream. -- `reserve()` - Reserves a spot in the output stream. Returns a Spot object. -- `flush()` - Flush output stream. Will wait for all open spots to close. - -Spot object has the follow methods: - -- `add(string)` - Adds `string` to the spot. -- `close()` - Close the spot. Must be called once done else rendering will be stuck. - -`context.user` will contain the following: - -- `request` - the server request object -- `routeOptions` - options for the route -- `routeData` - global data for the route -- `content` - the content from your route -- `mode` - Render Mode (nojs, noss, datass) -- `renderJs` - Render JS bundle -- `renderSs` - Do SSR -- `sriptNonce` - nonce for script tags -- `chunkNames` - webpack asset chunks -- `devCSSBundle` -- `devJSBundle` -- `jsChunk` -- `cssChunk` - -### Concurrent Token and Async Output - -Note that a token handler that takes `next` or returns Promise is only async, but not concurrent. Meaning the renderer will still wait for it to finish before continuing to the next token. - -If you want the renderer to continue while your token generates output concurrently, you have two options: - -1. Reserve a spot with `context.output.reserve()` and use `process.nextTick` to invoke another async function. - -2. Return a [node.js stream] for your output. - -For example, reserving a spot: - -```js -module.exports = function setup(options) { - return { - process: (context, token) => { - const spot = context.output.reserve(); - const concurrentProc = async () => { - const myData = await getMyData(); - spot.add(myData.stringify()); - spot.close(); - }; - process.nextTick(concurrentProc); - } - }; -}; -``` - -### routeData - -The `context.user.routeData` contains the following: - -- WEBPACK_DEV - true if webpack dev server running -- RENDER_JS - render JS bundle (could be override by request) -- RENDER_SS - do SSR (could be override by request) -- html - The html template -- assets - The webpack bundle assets -- devBundleBase - URL base for dev bundle -- prodBundleBase - URL base for prod bundle -- chunkSelector - callback to select webpack bundle -- iconStats - webpack icon stats -- htmlTokens - html template parsed into tokens - -[node.js stream]: https://nodejs.org/docs/latest-v10.x/api/stream.html#stream_stream diff --git a/packages/electrode-react-webapp/LICENSE b/packages/electrode-react-webapp/LICENSE deleted file mode 100644 index bd35f4e89..000000000 --- a/packages/electrode-react-webapp/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2016 WalmartLabs - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/packages/electrode-react-webapp/README.md b/packages/electrode-react-webapp/README.md deleted file mode 100644 index 4a4440928..000000000 --- a/packages/electrode-react-webapp/README.md +++ /dev/null @@ -1,290 +0,0 @@ -# Electrode React Webapp - -[![NPM version][npm-image]][npm-url] [![Dependency Status][daviddm-image]][daviddm-url] [![devDependency Status][daviddm-dev-image]][daviddm-dev-url] [![npm downloads][npm-downloads-image]][npm-downloads-url] - -This module helps render and serve your Electrode React application's `index.html`. It will handle doing server side rendering and embedding all the necessary JS and CSS bundles for your application. - -All the defaults are configured out of the box, but your index page is extensible. You can specify your own index template file with the `htmlFile` or `selectTemplate` options. - -See [design](/packages/electrode-react-webapp/DESIGN.md) for details on how the template can be extended. - -## Installing - -```bash -$ npm install electrode-react-webapp --save -``` - -## Usage - -### Registering with Hapi - -You can use this plugin by registering it with your Hapi server. - -```js -const reactWebapp = server.register({ - register: require("electrode-react-webapp"), - options: { - pageTitle: "My Awesome React WebApp", - paths: { - "/{args*}": { - content: "

Hello React!

" - } - } - } -}); -``` - -### Registering with electrode-server - -To use this with electrode-server, add to the config you pass to `electrode-server`: - -```js -const config = { - plugins: { - "electrode-react-webapp": { - options: { - pageTitle: "My Awesome React WebApp", - paths: { - "/{args*}": { - content: "

Hello React!

" - } - }, - unbundledJS: { - enterHead: [{ src: "http://cdn.com/js/lib.js" }] - } - } - } - } -}; - -require("electrode-server")(config); -``` - -## Default options - -This plugin has some default options but you can override them by setting your own value. - -The current defaults are: - -```js -{ - pageTitle: "Untitled Electrode Web Application", - webpackDev: process.env.WEBPACK_DEV === "true", - renderJS: true, - serverSideRendering: true, - htmlFile: "node_modules/electrode-react-webapp/lib/index.html", - devServer: { - host: "127.0.0.1", - port: "2992" - }, - paths: {}, - stats: "dist/server/stats.json" -} -``` - -## Options - -| name | type | default | description | -| -------------------------------------------- | ---------------------------- | -------- | ---------------------------------------------------------- | -| `pageTitle` | `String` | | The value to be shown in the browser's title bar | -| `htmlFile` | `String` | `*1` | Absolute or relative path to the HTML template. | -| [`selectTemplate`](#selecttemplate-function) | `Function` | | Callback to selecte HTML template base on `request` | -| `serverSideRendering` | `Boolean` | `false` | Toggle server side rendering. | -| `webpackDev` | `Boolean` | `false` | Running with webpack-dev-server | -| `paths` | `Object` | | Specify [route paths and content](#paths-and-content) | -| `unbundledJS` | `Object` | | [Load external JavaScript](#unbundledJS-details) into page | -| `devServer` | `Object` | | webpack Dev Server Options | -| `prodBundleBase` | `String` | `"/js/"` | Base path to the JavaScript, CSS and manifest bundles | -| `cspNonceValue` | [`varies`](#csp-nonce-value) | | Used to retrieve a CSP nonce value. | - -> `*1`: Default for `htmlFile` is to use this module's built-in [`index.html`](./lib/index.html) - -### Paths and Content - -Example: - -```js -{ - paths: { - "/test": { - // route specific options - } - } -} -``` - -Route speicific options can be: - -| name | type | description | -| ---------------------- | ---------- | ----------------------------------------------------------------- | -| `htmlFile` | `String` | Absolute or relative path to the HTML template file. | -| `templateFile` | `String` | | -| `insertTokenIds` | `Boolean` | | -| `pageTitle` | `String` | | -| `selectTemplate` | `Function` | Callback to selecte HTML template for the route base on `request` | -| `responseForBadStatus` | `Function` | | -| `responseForError` | `Function` | | - -| `content` | [`varies`](#content-details) | [Content generator](#content-details) for server-side rendering | -| `overrideOptions` | `Object` | Specify any config for the given path to override top level options | - -### `unbundledJS` Details - -Example: - -```js -{ - unbundledJS: { - enterHead: [], - preBundle: [] - } -} -``` - -- `enterHead` - Array of script entries to be inserted in `` before anything else -- `preBundle` - Array of script entries to be inserted in `` before the application's bundled JavaScript - -The script entries can be: - -- object - `{ src: "path to file" }` to insert a ` - * - * - * The output will be: - * - * - */ -module.exports = function groupScripts(data) { - const output = data.filter(x => x).reduce( - (acc, x) => { - const update = src => { - if (acc.src !== src || !acc.current) { - joinScripts(acc); - acc.current = [x]; - acc.src = src; - } else { - acc.current.push(x); - } - }; - - update(!!x.src); - - return acc; - }, - { src: false, scripts: [] } - ); - - joinScripts(output); - - return output; -}; diff --git a/packages/electrode-react-webapp/lib/hapi/index.js b/packages/electrode-react-webapp/lib/hapi/index.js deleted file mode 100644 index d71ab62f8..000000000 --- a/packages/electrode-react-webapp/lib/hapi/index.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; - -const { universalHapiPlugin, isHapi17 } = require("electrode-hapi-compat"); - -const hapi16 = require("./plugin16"); -const hapi17 = require("./plugin17"); -const pkg = require("../../package.json"); - -module.exports = universalHapiPlugin( - { - hapi16: hapi16.register, - hapi17: hapi17.register - }, - pkg -); - -const registerRoutes = isHapi17() ? hapi17.registerRoutes : hapi16.registerRoutes; - -// -// ensure extra props are not enumerable so Hapi's Joi about it -// -Object.defineProperty(module.exports, "registerRoutes", { - enumerable: false, - value: registerRoutes -}); diff --git a/packages/electrode-react-webapp/lib/hapi/plugin16.js b/packages/electrode-react-webapp/lib/hapi/plugin16.js deleted file mode 100644 index 9678d3d8d..000000000 --- a/packages/electrode-react-webapp/lib/hapi/plugin16.js +++ /dev/null @@ -1,83 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, max-params, max-statements, complexity */ - -const HttpStatusCodes = require("http-status-codes"); -const HttpStatus = require("../http-status"); - -const getDataHtml = data => (data.html !== undefined ? data.html : data); - -const DefaultHandleRoute = (request, reply, handler, content, routeOptions) => { - return handler({ - content, - mode: request.query.__mode || "", - template: request.indexPageTemplate, - request - }) - .then(context => { - const data = context.result; - const html = getDataHtml(data); - - if (html instanceof Error) { - throw html; - } - - let respond; - let status = data.status; - - if (data.verbatim || status === undefined) { - // if no status, fallback to context.status, and then 200 - status = status || context.status || HttpStatusCodes.OK; - respond = reply(html); - } else if (HttpStatus.redirect[status]) { - respond = reply.redirect(data.path); - return respond.code(status); - } else if (status >= HttpStatusCodes.OK && status < 300) { - respond = reply(html); - } else if (routeOptions.responseForBadStatus) { - const output = routeOptions.responseForBadStatus(request, routeOptions, data); - status = output.status; - respond = reply(output.html); - } else { - // should not reach here w/o html because user returned content - // would have to return a literal string that has no `.status` - // and html being undefined to be able to skip all the cases above - respond = reply(html); - } - - const response = context.user && context.user.response; - - if (response) { - Object.assign(respond.headers, response.headers); - } - - return respond.code(status); - }) - .catch(err => { - const output = routeOptions.responseForError(request, routeOptions, err); - - reply(output.html).code(output.status); - }); -}; - -const _registerRoutes = require("./register-routes"); - -const registerRoutes = (server, options) => _registerRoutes(server, options, DefaultHandleRoute); - -const register = (server, options, next) => { - try { - registerRoutes(server, options); - return next(); - } catch (err) { - return next(err); - } -}; - -const pkg = require("../../package.json"); - -register.attributes = { pkg }; - -module.exports = { - register, - registerRoutes -}; diff --git a/packages/electrode-react-webapp/lib/hapi/plugin17.js b/packages/electrode-react-webapp/lib/hapi/plugin17.js deleted file mode 100644 index aa131b3cc..000000000 --- a/packages/electrode-react-webapp/lib/hapi/plugin17.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, max-params, max-statements, complexity */ - -const HttpStatusCodes = require("http-status-codes"); -const HttpStatus = require("../http-status"); - -const getDataHtml = data => (data.html !== undefined ? data.html : data); - -const DefaultHandleRoute = (request, h, handler, content, routeOptions) => { - return handler({ - content, - mode: request.query.__mode || "", - template: request.indexPageTemplate, - request - }) - .then(context => { - if (context._intercepted) { - return context._intercepted.responseHandler(request, h, context); - } - - const data = context.result; - const html = getDataHtml(data); - - if (html instanceof Error) { - throw html; - } - - let respond; - let status = data.status; - - if (data.verbatim || status === undefined) { - // if no status, fallback to context.status, and then 200 - status = status || context.status || HttpStatusCodes.OK; - respond = h.response(html); - } else if (HttpStatus.redirect[status]) { - respond = h.redirect(data.path); - return respond.code(status); - } else if (status >= HttpStatusCodes.OK && status < 300) { - respond = h.response(html); - } else if (routeOptions.responseForBadStatus) { - const output = routeOptions.responseForBadStatus(request, routeOptions, data); - status = output.status; - respond = h.response(output.html); - } else { - // should not reach here w/o html because user returned content - // would have to return a literal string that has no `.status` - // and html being undefined to be able to skip all the cases above - respond = h.response(html); - } - - const response = context.user && context.user.response; - - if (response) { - Object.assign(respond.headers, response.headers); - } - - return respond.code(status); - }) - .catch(err => { - const output = routeOptions.responseForError(request, routeOptions, err); - - return h.response(output.html).code(output.status); - }); -}; - -const registerRoutes = require("./register-routes"); - -const register = (server, options) => registerRoutes(server, options, DefaultHandleRoute); - -const pkg = require("../../package.json"); - -module.exports = { - register, - registerRoutes: register, - pkg -}; diff --git a/packages/electrode-react-webapp/lib/hapi/register-routes.js b/packages/electrode-react-webapp/lib/hapi/register-routes.js deleted file mode 100644 index 99f27ed8f..000000000 --- a/packages/electrode-react-webapp/lib/hapi/register-routes.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, max-params, max-statements, complexity */ - -const _ = require("lodash"); -const Path = require("path"); -const ReactWebapp = require("../react-webapp"); -const { responseForError, responseForBadStatus } = require("../utils"); - -const registerRoutes = (server, options, defaultRouteHandler) => { - const registerOptions = ReactWebapp.setupOptions(options); - - _.each(registerOptions.paths, (pathData, path) => { - const routeOptions = ReactWebapp.setupPathOptions(registerOptions, path); - const routeHandler = ReactWebapp.makeRouteHandler(routeOptions); - - routeOptions.uiConfig = Object.assign( - { webappPrefix: "" }, - _.get(server, "settings.app.config.ui", {}), - routeOptions.uiConfig - ); - - const basePath = routeOptions.uiConfig.basePath || "/"; - - const handleRoute = options.handleRoute || defaultRouteHandler; - _.defaults(routeOptions, { responseForError, responseForBadStatus }); - const contentResolver = ReactWebapp.getContentResolver(registerOptions, pathData, path); - routeOptions.path = path; - routeOptions.pathData = pathData; - server.route({ - method: pathData.method || "GET", - path: Path.posix.join(basePath, path), - config: pathData.config || {}, - handler: (req, hOrReply) => { - const content = contentResolver(req.app.webpackDev); - return handleRoute(req, hOrReply, routeHandler, content, routeOptions); - } - }); - }); -}; - -module.exports = registerRoutes; diff --git a/packages/electrode-react-webapp/lib/http-status.js b/packages/electrode-react-webapp/lib/http-status.js deleted file mode 100644 index 471d1a045..000000000 --- a/packages/electrode-react-webapp/lib/http-status.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -const HttpStatusCodes = require("http-status-codes"); - -module.exports = { - // Status codes where we want to redirect the user - redirect: { - [HttpStatusCodes.MOVED_PERMANENTLY]: true, - [HttpStatusCodes.MOVED_TEMPORARILY]: true, - [HttpStatusCodes.PERMANENT_REDIRECT]: true, - [HttpStatusCodes.TEMPORARY_REDIRECT]: true - } -}; diff --git a/packages/electrode-react-webapp/lib/index.html b/packages/electrode-react-webapp/lib/index.html deleted file mode 100644 index cdb99e53c..000000000 --- a/packages/electrode-react-webapp/lib/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - diff --git a/packages/electrode-react-webapp/lib/jsx/Component.js b/packages/electrode-react-webapp/lib/jsx/Component.js deleted file mode 100644 index f1518de8c..000000000 --- a/packages/electrode-react-webapp/lib/jsx/Component.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; - -class Component { - constructor(props, context) { - this.props = props; - this.context = context; - } - - isComponent() { - return true; - } - - render() { - return "component"; - } -} - -module.exports = Component; diff --git a/packages/electrode-react-webapp/lib/jsx/IndexPage.js b/packages/electrode-react-webapp/lib/jsx/IndexPage.js deleted file mode 100644 index 51d1316ce..000000000 --- a/packages/electrode-react-webapp/lib/jsx/IndexPage.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -const Component = require("./Component"); - -class IndexPage extends Component { - static memoize(props) { - return ``; - } -} - -module.exports = IndexPage; diff --git a/packages/electrode-react-webapp/lib/jsx/JsxRenderer.js b/packages/electrode-react-webapp/lib/jsx/JsxRenderer.js deleted file mode 100644 index 657447647..000000000 --- a/packages/electrode-react-webapp/lib/jsx/JsxRenderer.js +++ /dev/null @@ -1,272 +0,0 @@ -"use strict"; - -/* eslint-disable max-statements, max-params, prefer-template, complexity */ - -const assert = require("assert"); -const _ = require("lodash"); -const loadHandler = require("../load-handler"); -const RenderContext = require("../render-context"); -const { omittedCloseTags, expandProps } = require("./utils"); -const Token = require("../token"); -const { TOKEN_HANDLER } = require("../symbols"); -const xaa = require("xaa"); - -class JsxRenderer { - constructor(options) { - this._options = options; - - this._tokenHandlers = [].concat(this._options.tokenHandlers).filter(x => x); - this._handlersMap = {}; - this._tokens = {}; - // the same context that gets passed to each token handler's setup function - this._handlerContext = _.merge( - { - user: { - // set routeOptions in user also for consistency - routeOptions: options.routeOptions - } - }, - options - ); - this._template = options.template; - } - - get insertTokenIds() { - return this._options.insertTokenIds; - } - - get templateFullPath() { - return this._options.templateFullPath; - } - - render(options) { - const defer = xaa.makeDefer(); - const context = new RenderContext(options, this); - - return xaa - .each(this._beforeRenders, r => r.beforeRender(context)) - .then(() => { - return this._render(this._template, context, 0, defer); - }) - .then(() => { - return defer.promise - .then(() => context.output.close()) - .then(result => { - /* istanbul ignore next */ - return xaa - .each(this._afterRenders, r => r.afterRender(context)) - .then(() => { - context.result = context.isVoidStop ? context.voidResult : result; - - return context; - }); - }); - }) - .catch(err => { - context.result = err; - return context; - }); - } - - _render(element, context, depth, defer) { - /* istanbul ignore next */ - const done = () => defer && defer.resolve(); - - if (context.isFullStop || context.isVoidStop) { - return done(); - } - - if (typeof element === "string") { - context.output.add(`${element}\n`); - return done(); - } else if (!element) { - return done(); - } - - let close; - - const handleClose = () => { - if (close) { - context.output.add(`${close}\n`); - } else { - context.output.add(`\n`); - } - - return defer && defer.resolve(); - }; - - const handleElementChildren = () => { - if (!element.children) { - return handleClose(); - } - - let ix = 0; - - const nextChild = () => { - if (ix >= element.children.length) { - return handleClose(); - } else { - const child = element.children[ix++]; - - try { - const promise = this._render(child, context, depth); - if (promise) { - return promise.then(nextChild); - } else { - return nextChild(); - } - } catch (err) { - if (defer) return defer.reject(err); - - throw err; - } - } - }; - - const promise = nextChild(); - - return !defer && promise; - }; - - const handleElementResult = rendered => { - if (!rendered) { - return handleClose(); - } - - if (typeof rendered === "string") { - context.output.add(rendered); - return handleClose(); - } else if (rendered.then) { - return rendered - .then(asyncRendered => { - return this._render(asyncRendered, context, depth + 1); - }) - .then(handleClose) - .catch(err => { - context.handleError(err); - }); - } else { - // TODO: is try/catch needed for this block? Need test case. - const promise = this._render(rendered, context, depth + 1); - if (promise) { - return promise - .then(asyncRendered => { - return this._render(asyncRendered, context, depth + 1); - }) - .then(handleClose); - } else { - return handleClose(); - } - } - }; - - if (element.memoize) { - context.output.add(`${element.memoize}\n`); - } else if (element.tag) { - if (!omittedCloseTags[element.tag]) { - close = ``; - context.output.add(`<${element.tag}${expandProps(element.props, context)}>`); - } else { - context.output.add(`<${element.tag}${expandProps(element.props, context)}/>`); - } - } else if (!element.type) { - return handleElementResult( - element(element.props, context, { element, depth, output: context.output }) - ); - } else if (element.Construct) { - const inst = new element.Construct(element.props, context); - return handleElementResult( - inst.render(element.props, context, { depth, output: context.output }) - ); - } else { - const r = element.type(element.props, context, { element, depth, output: context.output }); - return handleElementResult(r); - } - - return handleElementChildren(); - } - - initializeRenderer(reset) { - if (reset || !this._handlersLookup) { - this._initializeTokenHandlers(this._tokenHandlers); - this._handlersLookup = this._tokenHandlers.reverse(); - } - } - - _loadTokenHandler(path) { - const mod = loadHandler(path); - return mod(this._handlerContext, this); - } - - _applyTokenLoad(element, inst) { - inst.load(this._options); - - if (inst[TOKEN_HANDLER]) return; - - const handler = this._handlersLookup.find(h => h.tokens.hasOwnProperty(inst.id)); - if (!handler) return; - - const tkFunc = handler.tokens[inst.id]; - if (typeof tkFunc === "function") { - inst.setHandler(tkFunc); - } else { - element.memoize = tkFunc; - } - } - - setupTokenInst(element, scope, forRequire) { - let tokenInst; - let memId; - - if (scope.depth < 1) { - memId = `${element.props._id}_${element.id}`; - tokenInst = this._tokens[memId]; - - if (tokenInst) { - return tokenInst; - } - } - - const id = forRequire ? `require(${element.props._id})` : element.props._id; - - tokenInst = new Token(id, 0, element.props, this.templateFullPath); - - if (memId) { - this._tokens[memId] = tokenInst; - } - - this._applyTokenLoad(element, tokenInst); - - return tokenInst; - } - - getTokenInst(element) { - return this._tokens[element.props._id]; - } - - _initializeTokenHandlers(filenames) { - this._tokenHandlers = filenames.map(fname => { - let handler; - if (typeof fname === "string") { - handler = this._loadTokenHandler(fname); - } else { - handler = fname; - assert(handler.name, "electrode-react-webapp Template token handler missing name"); - } - if (!handler.name) { - handler = { - name: fname, - tokens: handler - }; - } - assert(handler.tokens, "electrode-react-webapp Template token handler missing tokens"); - this._handlersMap[handler.name] = handler; - return handler; - }); - - this._beforeRenders = this._tokenHandlers.filter(x => x.beforeRender); - this._afterRenders = this._tokenHandlers.filter(x => x.afterRender); - } -} - -module.exports = JsxRenderer; diff --git a/packages/electrode-react-webapp/lib/jsx/Literal.js b/packages/electrode-react-webapp/lib/jsx/Literal.js deleted file mode 100644 index e29d22a0a..000000000 --- a/packages/electrode-react-webapp/lib/jsx/Literal.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; - -/* eslint-disable no-unused-vars, no-magic-numbers */ - -const Path = require("path"); -const Fs = require("fs"); -const _ = require("lodash"); - -const MEMOIZE = {}; - -function Literal(props, context, scope) { - const renderer = context.asyncTemplate; - - let data; - - if (props.file) { - const fp = Path.resolve(props.file); - - if (MEMOIZE[fp]) { - data = MEMOIZE[fp]; - } else { - try { - data = Fs.readFileSync(fp, _.get(props, "encoding", "utf8")); - } catch (err) { - const cwd = process.cwd(); - /* istanbul ignore next */ - const msg = cwd.length > 3 ? err.message.replace(cwd, "CWD") : err.message; - data = `

Literal reading file failed: ${msg}

`; - } - } - - if (props._memoize !== false) { - MEMOIZE[fp] = data; - } - } else { - data = "

Literal props missing file

"; - } - - return data; -} - -module.exports = Literal; diff --git a/packages/electrode-react-webapp/lib/jsx/Require.js b/packages/electrode-react-webapp/lib/jsx/Require.js deleted file mode 100644 index 63bd77e5c..000000000 --- a/packages/electrode-react-webapp/lib/jsx/Require.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -const processToken = require("./process-token"); - -function Require(props, context, scope) { - return processToken(props, context, scope, true); -} - -module.exports = Require; diff --git a/packages/electrode-react-webapp/lib/jsx/Token.js b/packages/electrode-react-webapp/lib/jsx/Token.js deleted file mode 100644 index 187acb462..000000000 --- a/packages/electrode-react-webapp/lib/jsx/Token.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -const processToken = require("./process-token"); - -function Token(props, context, scope) { - return processToken(props, context, scope); -} - -module.exports = Token; diff --git a/packages/electrode-react-webapp/lib/jsx/index.js b/packages/electrode-react-webapp/lib/jsx/index.js deleted file mode 100644 index 04331874c..000000000 --- a/packages/electrode-react-webapp/lib/jsx/index.js +++ /dev/null @@ -1,55 +0,0 @@ -"use strict"; - -const Component = require("./Component"); -const Token = require("./Token"); -const IndexPage = require("./IndexPage"); -const Require = require("./Require"); -const Literal = require("./Literal"); -const JsxRenderer = require("./JsxRenderer"); - -let ELEMENT_ID = 0; - -function createElement(type, props, ...children) { - children = children.length > 0 ? children : undefined; - - if (children) { - children = children.reduce((a, c) => a.concat(c), []); - } - - if (!props) { - props = { children }; - } else { - props = Object.assign({ children }, props); - } - - const element = { - id: ELEMENT_ID++, - type, - children, - props - }; - - const literal = typeof type === "string"; - - if (literal) { - element.tag = type.toLowerCase(); - } else if (type.prototype && type.prototype.isComponent) { - element.Construct = type; - } - - if (!literal && type.memoize && props._memoize !== false) { - element.memoize = type.memoize(props, children); - } - - return element; -} - -module.exports = { - Component, - Token, - Literal, - IndexPage, - Require, - JsxRenderer, - createElement -}; diff --git a/packages/electrode-react-webapp/lib/jsx/process-token.js b/packages/electrode-react-webapp/lib/jsx/process-token.js deleted file mode 100644 index 273882107..000000000 --- a/packages/electrode-react-webapp/lib/jsx/process-token.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -/* eslint-disable max-params */ - -const { TOKEN_HANDLER } = require("../symbols"); - -const _ = require("lodash"); - -function processToken(props, context, scope, forRequire = false) { - const renderer = context.asyncTemplate; - - const tokenInst = renderer.setupTokenInst(scope.element, scope, forRequire); - - if (scope.element.memoize) { - return scope.output.add(scope.element.memoize); - } - - if (tokenInst[TOKEN_HANDLER] === null) { - if (renderer.insertTokenIds) { - return ``; - } - return null; - } - - const insertTokenIds = - props._insertTokenIds !== false && (props._insertTokenIds || renderer.insertTokenIds); - - if (insertTokenIds) { - scope.output.add( - `\n` - ); - } - - const r = tokenInst[TOKEN_HANDLER](context, tokenInst); - return context.handleTokenResult(tokenInst.id, r, err => { - if (insertTokenIds) { - scope.output.add(``); - } - // if err, then handler has caused/returned an error - if (err) { - throw err; - } - }); -} - -module.exports = processToken; diff --git a/packages/electrode-react-webapp/lib/jsx/utils.js b/packages/electrode-react-webapp/lib/jsx/utils.js deleted file mode 100644 index c721d0a65..000000000 --- a/packages/electrode-react-webapp/lib/jsx/utils.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; - -function expandProps(props, context) { - let s = ""; - - for (const key in props) { - if (key !== "children") { - let v = props[key]; - if (typeof v === "function") { - v = v(context); - } - - s = `${s} ${key}="${v}"`; - } - } - return s; -} - -module.exports = { - expandProps, - - // For HTML, certain tags should omit their close tag. We keep a whitelist for - // those special-case tags. - - // copied from react-dom/server - - omittedCloseTags: { - area: true, - base: true, - br: true, - col: true, - embed: true, - hr: true, - img: true, - input: true, - keygen: true, - link: true, - meta: true, - param: true, - source: true, - track: true, - wbr: true - // NOTE: menuitem's close tag should be omitted, but that causes problems. - } -}; diff --git a/packages/electrode-react-webapp/lib/koa/index.js b/packages/electrode-react-webapp/lib/koa/index.js deleted file mode 100644 index bf4821ad7..000000000 --- a/packages/electrode-react-webapp/lib/koa/index.js +++ /dev/null @@ -1,84 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, max-params */ - -const _ = require("lodash"); -const ReactWebapp = require("../react-webapp"); -const HttpStatus = require("../http-status"); -const { responseForError, responseForBadStatus } = require("../utils"); - -const getDataHtml = data => (data.html !== undefined ? data.html : data); - -const DefaultHandleRoute = (request, response, handler, content, routeOptions) => { - return handler({ content, mode: request.query.__mode || "", request }) - .then(context => { - const data = context.result; - if (data instanceof Error) { - throw data; - } - - if (data.status === undefined) { - response.status = 200; - response.body = data; - } else if (HttpStatus.redirect[data.status]) { - response.redirect(data.path); - response.status = data.status; - } else if (data.status >= 200 && data.status < 300) { - response.body = getDataHtml(data); - } else if (routeOptions.responseForBadStatus) { - const output = routeOptions.responseForBadStatus(request, routeOptions, data); - response.status = output.status; - response.body = output.html; - } else { - response.status = data.status; - response.body = getDataHtml(data); - } - return response; - }) - .catch(err => { - const output = routeOptions.responseForError(request, routeOptions, err); - response.status = output.status; - response.body = output.html; - }); -}; - -const registerRoutes = (router, options, next = () => {}) => { - const registerOptions = ReactWebapp.setupOptions(options); - - _.each(registerOptions.paths, (v, path) => { - const routeOptions = _.defaults({ htmlFile: v.htmlFile }, registerOptions); - const routeHandler = ReactWebapp.makeRouteHandler(routeOptions); - - routeOptions.uiConfig = Object.assign( - {}, - _.get(router, "config.ui", {}), - routeOptions.uiConfig - ); - - const handleRoute = options.handleRoute || DefaultHandleRoute; - _.defaults(routeOptions, { responseForError, responseForBadStatus }); - - let methods = v.method || ["GET"]; - if (!Array.isArray(methods)) { - methods = [methods]; - } - - const contentResolver = ReactWebapp.getContentResolver(registerOptions, v.content, path); - - _.each(methods, method => { - if (method === "*") { - method = "ALL"; - } - - router[method.toLowerCase()](path, async (ctx, next1) => { - const content = contentResolver(ctx.webpackDev); - await handleRoute(ctx.request, ctx.response, routeHandler, content, routeOptions); - return next1(); - }); - }); - }); - - next(); -}; - -module.exports = registerRoutes; diff --git a/packages/electrode-react-webapp/lib/load-handler.js b/packages/electrode-react-webapp/lib/load-handler.js deleted file mode 100644 index 66b840555..000000000 --- a/packages/electrode-react-webapp/lib/load-handler.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, no-console */ - -const Path = require("path"); -const requireAt = require("require-at"); -const optionalRequire = require("optional-require"); - -const failLoadTokenModule = (m, e) => { - console.error( - `error: electrode-react-webapp failed to load token process module ${m}`, - `Error:`, - e - ); - return () => ({ process: () => `\ntoken process module ${m} failed to load\n` }); -}; - -const notFoundLoadTokenModule = m => { - console.error(`error: electrode-react-webapp can't find token process module ${m}`); - return () => ({ process: () => `\ntoken process module ${m} not found\n` }); -}; - -module.exports = (path, tmplDir, customCall) => { - const tokenMod = optionalRequire(requireAt(Path.resolve(tmplDir || "")))(path, { - fail: e => failLoadTokenModule(path, e), - notFound: () => notFoundLoadTokenModule(path) - }); - - if (typeof tokenMod === "function") { - return tokenMod; - } - if (tokenMod.tokenHandler) { - return tokenMod.tokenHandler; - } - if (tokenMod.default) { - return tokenMod.default; - } - - if (customCall && tokenMod[customCall]) { - return tokenMod; - } - throw new Error( - `electrode-react-webapp: token module invalid - should export a function directly \ -or as 'default' or 'tokenHandler'` - ); -}; diff --git a/packages/electrode-react-webapp/lib/react-webapp.js b/packages/electrode-react-webapp/lib/react-webapp.js deleted file mode 100644 index c5a4fab47..000000000 --- a/packages/electrode-react-webapp/lib/react-webapp.js +++ /dev/null @@ -1,313 +0,0 @@ -"use strict"; - -/* eslint-disable max-statements, global-require */ - -const _ = require("lodash"); -const Path = require("path"); -const assert = require("assert"); -const AsyncTemplate = require("./async-template"); -const { JsxRenderer } = require("./jsx"); - -const { - getOtherStats, - getOtherAssets, - resolveChunkSelector, - loadAssetsFromStats, - getStatsPath, - invokeTemplateProcessor, - makeDevBundleBase -} = require("./utils"); - -const otherStats = getOtherStats(); - -function initializeTemplate( - { htmlFile, templateFile, tokenHandlers, cacheId, cacheKey, options }, - routeOptions -) { - const tmplFile = templateFile || htmlFile; - cacheKey = cacheKey || (cacheId && `${tmplFile}#${cacheId}`) || tmplFile; - - let asyncTemplate = routeOptions._templateCache[cacheKey]; - if (asyncTemplate) { - return asyncTemplate; - } - - if (options) { - routeOptions = Object.assign({}, routeOptions, options); - } - - const userTokenHandlers = [] - .concat(tokenHandlers, routeOptions.tokenHandler, routeOptions.tokenHandlers) - .filter(x => x); - - let finalTokenHandlers = userTokenHandlers; - - // Inject the built-in react/token-handlers if it is not in user's handlers - // and replaceTokenHandlers option is false - if (!routeOptions.replaceTokenHandlers) { - const reactTokenHandlers = Path.join(__dirname, "react/token-handlers"); - finalTokenHandlers = - userTokenHandlers.indexOf(reactTokenHandlers) < 0 - ? [reactTokenHandlers].concat(userTokenHandlers) - : userTokenHandlers; - } - - if (!templateFile) { - asyncTemplate = new AsyncTemplate({ - htmlFile, - tokenHandlers: finalTokenHandlers.filter(x => x), - insertTokenIds: routeOptions.insertTokenIds, - routeOptions - }); - - invokeTemplateProcessor(asyncTemplate, routeOptions); - asyncTemplate.initializeRenderer(); - } else { - const templateFullPath = require.resolve(tmplFile); - const template = require(tmplFile); - asyncTemplate = new JsxRenderer({ - templateFullPath: Path.dirname(templateFullPath), - template: _.get(template, "default", template), - tokenHandlers: finalTokenHandlers.filter(x => x), - insertTokenIds: routeOptions.insertTokenIds, - routeOptions - }); - asyncTemplate.initializeRenderer(); - } - - return (routeOptions._templateCache[cacheKey] = asyncTemplate); -} - -function makeRouteHandler(routeOptions) { - routeOptions._templateCache = {}; - let defaultSelection; - - if (routeOptions.templateFile) { - defaultSelection = { - templateFile: - typeof routeOptions.templateFile === "string" - ? Path.resolve(routeOptions.templateFile) - : Path.join(__dirname, "../template/index") - }; - } else { - defaultSelection = { htmlFile: routeOptions.htmlFile }; - } - - const render = (options, templateSelection) => { - let selection = templateSelection || defaultSelection; - if (templateSelection && !templateSelection.templateFile && !templateSelection.htmlFile) { - selection = Object.assign({}, templateSelection, defaultSelection); - } - const asyncTemplate = initializeTemplate(selection, routeOptions); - return asyncTemplate.render(options); - }; - - return options => { - if (routeOptions.selectTemplate) { - const selection = routeOptions.selectTemplate(options.request, routeOptions); - - if (selection && selection.then) { - return selection.then(x => render(options, x)); - } - - return render(options, selection); - } - - const asyncTemplate = initializeTemplate(defaultSelection, routeOptions); - return asyncTemplate.render(options); - }; -} - -const setupOptions = options => { - const https = process.env.WEBPACK_DEV_HTTPS && process.env.WEBPACK_DEV_HTTPS !== "false"; - - const pluginOptionsDefaults = { - pageTitle: "Untitled Electrode Web Application", - webpackDev: process.env.WEBPACK_DEV === "true", - renderJS: true, - serverSideRendering: true, - htmlFile: Path.join(__dirname, "index.html"), - devServer: { - protocol: https ? "https" : "http", - host: process.env.WEBPACK_DEV_HOST || process.env.WEBPACK_HOST || "localhost", - port: process.env.WEBPACK_DEV_PORT || "2992", - https - }, - unbundledJS: { - enterHead: [], - preBundle: [], - postBundle: [] - }, - paths: {}, - stats: "dist/server/stats.json", - otherStats, - iconStats: "dist/server/iconstats.json", - criticalCSS: "dist/js/critical.css", - buildArtifacts: ".build", - prodBundleBase: "/js/", - cspNonceValue: undefined - }; - - const pluginOptions = _.defaultsDeep({}, options, pluginOptionsDefaults); - const chunkSelector = resolveChunkSelector(pluginOptions); - const devBundleBase = makeDevBundleBase(pluginOptions.devServer); - const statsPath = getStatsPath(pluginOptions.stats, pluginOptions.buildArtifacts); - - const assets = loadAssetsFromStats(statsPath); - const otherAssets = getOtherAssets(pluginOptions); - pluginOptions.__internals = _.defaultsDeep({}, pluginOptions.__internals, { - assets, - otherAssets, - chunkSelector, - devBundleBase - }); - - return pluginOptions; -}; - -const pathSpecificOptions = [ - "htmlFile", - "templateFile", - "insertTokenIds", - "pageTitle", - "selectTemplate", - "responseForBadStatus", - "responseForError" -]; - -const setupPathOptions = (routeOptions, path) => { - const pathData = _.get(routeOptions, ["paths", path], {}); - const pathOverride = _.get(routeOptions, ["paths", path, "overrideOptions"], {}); - const pathOptions = pathData.options; - return _.defaultsDeep( - _.pick(pathData, pathSpecificOptions), - { - tokenHandler: [].concat(routeOptions.tokenHandler, pathData.tokenHandler), - tokenHandlers: [].concat(routeOptions.tokenHandlers, pathData.tokenHandlers) - }, - pathOptions, - _.omit(pathOverride, "paths"), - routeOptions - ); -}; - -// -// The route path can supply: -// -// - a literal string -// - a function -// - an object -// -// If it's an object: -// -- if it doesn't contain content, then it's assume to be the content. -// -// If it contains content, then it can contain: -// -// - method: HTTP method for the route -// - config: route config (applicable for framework like Hapi) -// - content: second level field to define content -// -// content can be: -// -// - a literal string -// - a function -// - an object -// -// If content is an object, it can contain module, a path to the JS module to require -// to load the content. -// -const resolveContent = (pathData, xrequire) => { - const resolveTime = Date.now(); - - let content = pathData; - - // If it's an object, see if contains content field - if (_.isObject(pathData) && pathData.hasOwnProperty("content")) { - content = pathData.content; - } - - if (!content && !_.isString(content)) return null; - - // content has module field, require it. - if (!_.isString(content) && !_.isFunction(content) && content.module) { - const mod = content.module.startsWith(".") ? Path.resolve(content.module) : content.module; - - xrequire = xrequire || require; - - try { - return { - fullPath: xrequire.resolve(mod), - xrequire, - resolveTime, - content: xrequire(mod) - }; - } catch (error) { - const msg = `electrode-react-webapp: failed to load SSR content from module ${mod}`; - console.error(msg, "\n", error); // eslint-disable-line - return { - fullPath: null, - error, - resolveTime, - content: `

electrode-react-webapp: SSR failed

-

${msg}

-
${error.stack}
-` - }; - } - } - - return { - fullPath: null, - resolveTime, - content - }; -}; - -const getContentResolver = (registerOptions, pathData, path) => { - let resolved; - - const resolveWithDev = (webpackDev, xrequire) => { - if (!webpackDev.valid) { - resolved = resolveContent(""); - } else if (webpackDev.hasErrors) { - resolved = resolveContent(""); - } else if (!resolved || resolved.resolveTime < webpackDev.compileTime) { - if (resolved && resolved.fullPath) { - delete resolved.xrequire.cache[resolved.fullPath]; - } - resolved = resolveContent(pathData, xrequire); - } - - return resolved.content; - }; - - return (webpackDev, xrequire) => { - if (webpackDev && registerOptions.serverSideRendering !== false) { - return resolveWithDev(webpackDev, xrequire); - } - - if (resolved) return resolved.content; - - if (registerOptions.serverSideRendering !== false) { - resolved = resolveContent(pathData); - assert(resolved, `You must define content for the webapp plugin path ${path}`); - } else { - resolved = { - content: { - status: 200, - html: "" - } - }; - } - - return resolved.content; - }; -}; - -module.exports = { - setupOptions, - setupPathOptions, - makeRouteHandler, - resolveContent, - getContentResolver -}; diff --git a/packages/electrode-react-webapp/lib/react/content.js b/packages/electrode-react-webapp/lib/react/content.js deleted file mode 100644 index dc17a2b5a..000000000 --- a/packages/electrode-react-webapp/lib/react/content.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; - -const Fs = require("fs"); -const Path = require("path"); -const HttpStatusCodes = require("http-status-codes"); - -const HTTP_ERROR_500 = 500; - -function getContent(renderSs, options, context) { - let userContent = options.content; - - // prepare user content for container of SSR output - - if (typeof userContent === "string") { - return Promise.resolve({ status: 200, html: userContent }); - } - - if (typeof userContent !== "function") return Promise.resolve(userContent); - - if (!renderSs) return Promise.resolve({ status: 200, html: "" }); - - // invoke user content as a function, which could return any content - // as static html or generated from react's renderToString - userContent = userContent(options.request, options, context); - - if (userContent.catch) { - // user function needs to generate the content async, so wait for it. - return userContent.catch(err => { - if (!err.status) err.status = HTTP_ERROR_500; - throw err; - }); - } - - return Promise.resolve(userContent); -} - -function transformOutput(result, context) { - const content = context.user.content; - if (content && content.status !== HttpStatusCodes.OK) { - return { - verbatim: content.verbatim, - status: content.status, - path: content.path, - store: content.store, - html: result - }; - } - - return result; -} - -const htmlifyScripts = (scripts, scriptNonce) => { - return scripts - .map(x => - typeof x === "string" - ? `${x}\n` - : x.map(n => ``).join("\n") - ) - .join("\n"); -}; - -const loadElectrodeDllAssets = routeOptions => { - const tag = process.env.NODE_ENV === "production" ? "" : ".dev"; - try { - const file = Path.resolve( - routeOptions.electrodeDllAssetsPath || `dist/electrode-dll-assets${tag}.json` - ); - return JSON.parse(Fs.readFileSync(file)); - } catch (err) { - return {}; - } -}; - -const makeElectrodeDllScripts = (dllAssets, nonce) => { - const scripts = []; - for (const modName in dllAssets) { - const cdnMapping = dllAssets[modName].cdnMapping; - for (const bundle in cdnMapping) { - scripts.push({ src: cdnMapping[bundle] }); - } - } - - return htmlifyScripts([scripts], nonce); -}; - -module.exports = { - getContent, - transformOutput, - htmlifyScripts, - loadElectrodeDllAssets, - makeElectrodeDllScripts -}; diff --git a/packages/electrode-react-webapp/lib/react/handlers/prefetch-bundles.js b/packages/electrode-react-webapp/lib/react/handlers/prefetch-bundles.js deleted file mode 100644 index 702f44f7d..000000000 --- a/packages/electrode-react-webapp/lib/react/handlers/prefetch-bundles.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = context => { - const content = context.user.content; - if (!content || !content.prefetch) return ""; - - // allow user to include the `; - } else { - return content.prefetch; - } -}; diff --git a/packages/electrode-react-webapp/lib/react/token-handlers.js b/packages/electrode-react-webapp/lib/react/token-handlers.js deleted file mode 100644 index 8f7f25231..000000000 --- a/packages/electrode-react-webapp/lib/react/token-handlers.js +++ /dev/null @@ -1,241 +0,0 @@ -"use strict"; - -/* eslint-disable max-statements, max-depth */ - -const groupScripts = require("../group-scripts"); - -const { - getIconStats, - getCriticalCSS, - getDevCssBundle, - getDevJsBundle, - getProdBundles, - processRenderSsMode, - getCspNonce, - getBundleJsNameByQuery, - isReadableStream -} = require("../utils"); - -const { - getContent, - transformOutput, - htmlifyScripts, - loadElectrodeDllAssets, - makeElectrodeDllScripts -} = require("./content"); - -const prefetchBundles = require("./handlers/prefetch-bundles"); -const CONTENT_MARKER = "SSR_CONTENT"; -const HEADER_BUNDLE_MARKER = "WEBAPP_HEADER_BUNDLES"; -const BODY_BUNDLE_MARKER = "WEBAPP_BODY_BUNDLES"; -const DLL_BUNDLE_MARKER = "WEBAPP_DLL_BUNDLES"; -const TITLE_MARKER = "PAGE_TITLE"; -const PREFETCH_MARKER = "PREFETCH_BUNDLES"; -const META_TAGS_MARKER = "META_TAGS"; -const CRITICAL_CSS_MARKER = "CRITICAL_CSS"; -const APP_CONFIG_DATA_MARKER = "APP_CONFIG_DATA"; -const WEBAPP_START_SCRIPT_MARKER = "WEBAPP_START_SCRIPT"; - -module.exports = function setup(handlerContext /*, asyncTemplate*/) { - const routeOptions = handlerContext.user.routeOptions; - - const WEBPACK_DEV = routeOptions.webpackDev; - const RENDER_JS = routeOptions.renderJS; - const RENDER_SS = routeOptions.serverSideRendering; - const assets = routeOptions.__internals.assets; - const otherAssets = routeOptions.__internals.otherAssets; - const devBundleBase = routeOptions.__internals.devBundleBase; - const prodBundleBase = routeOptions.prodBundleBase; - const chunkSelector = routeOptions.__internals.chunkSelector; - const iconStats = getIconStats(routeOptions.iconStats); - const criticalCSS = getCriticalCSS(routeOptions.criticalCSS); - - const routeData = { - WEBPACK_DEV, - RENDER_JS, - RENDER_SS, - assets, - otherAssets, - devBundleBase, - prodBundleBase, - chunkSelector, - iconStats, - criticalCSS - }; - - handlerContext.user.routeData = routeData; - - const bundleManifest = () => { - if (!assets.manifest) { - return ""; - } - - return WEBPACK_DEV - ? `${devBundleBase}${assets.manifest}` - : `${prodBundleBase}${assets.manifest}`; - }; - - const bundleJs = data => { - if (!data.renderJs) { - return ""; - } - if (WEBPACK_DEV) { - return data.devJSBundle; - } else if (data.jsChunk) { - const bundleJsName = getBundleJsNameByQuery(data, otherAssets); - return `${prodBundleBase}${bundleJsName}`; - } else { - return ""; - } - }; - - const INITIALIZE = context => { - const options = context.options; - const request = options.request; - const mode = options.mode; - const renderSs = processRenderSsMode(request, RENDER_SS, mode); - - return getContent(renderSs, options, context).then(content => { - if (content.render === false || content.html === undefined) { - return context.voidStop(content); - } - - const chunkNames = chunkSelector(request); - - const devCSSBundle = getDevCssBundle(chunkNames, routeData); - const devJSBundle = getDevJsBundle(chunkNames, routeData); - - const { jsChunk, cssChunk } = getProdBundles(chunkNames, routeData); - const { scriptNonce, styleNonce } = getCspNonce(request, routeOptions.cspNonceValue); - - const renderJs = RENDER_JS && mode !== "nojs"; - - context.user = { - request: options.request, - response: { - headers: {} - }, - routeOptions, - routeData, - content, - mode, - renderJs, - renderSs, - scriptNonce, - styleNonce, - chunkNames, - devCSSBundle, - devJSBundle, - jsChunk, - cssChunk - }; - - if (content.useStream || isReadableStream(content.html)) { - context.setMunchyOutput(); - } - - context.setOutputTransform(transformOutput); - - return context; - }); - }; - - const windowConfigKey = routeOptions.uiConfigKey || "_config"; - - const tokenHandlers = { - [CONTENT_MARKER]: context => { - return (context.user.content && context.user.content.html) || ""; - }, - - [TITLE_MARKER]: () => { - return `${routeOptions.pageTitle}`; - }, - - [APP_CONFIG_DATA_MARKER]: context => { - const { webappPrefix } = context.user.routeOptions.uiConfig; - const key = `${webappPrefix}${windowConfigKey}`; - - return ``; - }, - - [HEADER_BUNDLE_MARKER]: context => { - const manifest = bundleManifest(); - const manifestLink = manifest ? `\n` : ""; - const css = [].concat(WEBPACK_DEV ? context.user.devCSSBundle : context.user.cssChunk); - - const cssLink = css.reduce((acc, file) => { - file = WEBPACK_DEV ? file : prodBundleBase + file.name; - return `${acc}`; - }, ""); - - const htmlScripts = htmlifyScripts( - groupScripts(routeOptions.unbundledJS.enterHead).scripts, - context.user.scriptNonce - ); - - return `${manifestLink}${cssLink}${htmlScripts}`; - }, - - [BODY_BUNDLE_MARKER]: context => { - context.user.query = context.user.request.query; - const js = bundleJs(context.user); - const jsLink = js ? { src: js } : ""; - - const ins = routeOptions.unbundledJS.preBundle.concat( - jsLink, - routeOptions.unbundledJS.postBundle - ); - const htmlScripts = htmlifyScripts(groupScripts(ins).scripts, context.user.scriptNonce); - - return `${htmlScripts}`; - }, - - [DLL_BUNDLE_MARKER]: context => { - if (WEBPACK_DEV) { - return makeElectrodeDllScripts(loadElectrodeDllAssets(context.user.routeOptions)); - } - - if (context.user.routeData.dllAssetScripts === undefined) { - context.user.routeData.dllAssetScripts = makeElectrodeDllScripts( - loadElectrodeDllAssets(context.user.routeOptions), - context.user.scriptNonce - ); - } - - return context.user.routeData.dllAssetScripts; - }, - - [PREFETCH_MARKER]: prefetchBundles, - - [META_TAGS_MARKER]: iconStats, - - [CRITICAL_CSS_MARKER]: context => { - return criticalCSS ? `${criticalCSS}` : ""; - }, - - [WEBAPP_START_SCRIPT_MARKER]: context => { - const { webappPrefix } = context.user.routeOptions.uiConfig; - /* istanbul ignore next */ - const startFuncName = webappPrefix ? `${webappPrefix}WebappStart` : "webappStart"; - return ``; - }, - - INITIALIZE, - HEAD_INITIALIZE: null, - HEAD_CLOSED: null, - AFTER_SSR_CONTENT: null, - BODY_CLOSED: null, - HTML_CLOSED: null - }; - - return { - name: "electrode-react-token-handlers", - routeOptions, - routeData, - tokens: tokenHandlers - }; -}; diff --git a/packages/electrode-react-webapp/lib/render-context.js b/packages/electrode-react-webapp/lib/render-context.js deleted file mode 100644 index 31ca24a3f..000000000 --- a/packages/electrode-react-webapp/lib/render-context.js +++ /dev/null @@ -1,133 +0,0 @@ -"use strict"; - -const RenderOutput = require("./render-output"); -const Munchy = require("munchy"); -const utils = require("./utils"); - -class RenderContext { - constructor(options, asyncTemplate) { - this.user = false; - this.options = options; - this.output = new RenderOutput(this); - this.asyncTemplate = asyncTemplate; - this._handlersMap = asyncTemplate.handlersMap; - this.handleError = err => !this.stop && this.voidStop(err); - this.transform = x => x; - this._stop = 0; - } - - // return a token handler by name - getTokenHandler(name) { - return this._handlersMap[name]; - } - - // return the tokens object of a handler by name - getTokens(name) { - return this._handlersMap[name].tokens; - } - - // set a send callback for receiving the render output - // Note: cannot be used with setOutputTransform - setOutputSend(send) { - this.send = send; - } - - setMunchyOutput(munchy) { - this.munchy = munchy || new Munchy({ handleStreamError: utils.munchyHandleStreamError }); - } - - // set a callback to take the output result and transform it - // Note: cannot be used with setOutputSend - setOutputTransform(transform) { - this.transform = result => transform(result, this); - } - - get stop() { - return this._stop; - } - - get status() { - return this._status; - } - - set status(s) { - this._status = s; - } - - /* - * Allow user to intercept handling of the rendering flow and take over the response with handler - * - * - If no handler provided, then the error is returned. - */ - intercept({ responseHandler }) { - this._intercepted = { responseHandler }; - throw new Error("electrode-react-webapp: render-context - user intercepted"); - } - - // - // set this any time to fully stop and close rendering - // stop modes: - // 1 - only process string tokens - // 2 - fully stop immediately, no more token processing - // 3 - completely void any render output and stop immediately, - // replace output with result. This only works if output - // is buffered and not streaming out immediately. - // - set stop(f) { - this._stop = f; - } - - fullStop() { - this._stop = RenderContext.FULL_STOP; - } - - get isFullStop() { - return this._stop === RenderContext.FULL_STOP; - } - - voidStop(result) { - this._stop = RenderContext.VOID_STOP; - this.voidResult = result; - } - - get isVoidStop() { - return this._stop === RenderContext.VOID_STOP; - } - - softStop() { - this._stop = RenderContext.SOFT_STOP; - } - - get isSoftStop() { - return this._stop === RenderContext.SOFT_STOP; - } - - // helper to take the result of a token handler and process the returned result - // according to if it's async or not, and then invoke the callback - // - If it's a Promise (async), then wait for it before invoking callback - // - else add it to output if it's a string, and then invoke callback - // Note: If it's async, then the result from the Promise is not checked because - // we don't know how token handler wants to deal with it. - handleTokenResult(id, result, cb) { - const addToOutput = res => { - // if it's a string, buffer, or stream, then add to output - if (typeof res === "string" || Buffer.isBuffer(res) || utils.isReadableStream(res)) { - this.output.add(res); - } - // ignore other return value types - return cb(); - }; - // it's a promise, wait for it before invoking callback - if (result && result.then) { - return result.then(addToOutput).catch(cb); - } else { - return addToOutput(result); - } - } -} - -RenderContext.VOID_STOP = 3; -RenderContext.FULL_STOP = 2; -RenderContext.SOFT_STOP = 1; - -module.exports = RenderContext; diff --git a/packages/electrode-react-webapp/lib/render-execute.js b/packages/electrode-react-webapp/lib/render-execute.js deleted file mode 100644 index 0a2fa34f2..000000000 --- a/packages/electrode-react-webapp/lib/render-execute.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; - -/* eslint-disable complexity */ - -const { TOKEN_HANDLER } = require("./symbols"); - -const executeSteps = { - STEP_HANDLER: 0, - STEP_STR_TOKEN: 1, - STEP_NO_HANDLER: 2, - STEP_LITERAL_HANDLER: 3 -}; - -const { STEP_HANDLER, STEP_STR_TOKEN, STEP_NO_HANDLER, STEP_LITERAL_HANDLER } = executeSteps; - -function renderNext(err, xt) { - const { renderSteps, context } = xt; - if (err) { - context.handleError(err); - } - - const insertTokenId = tk => { - context.output.add(`\n`); - }; - - const insertTokenIdEnd = tk => { - context.output.add(`\n`); - }; - - if (context.isFullStop || context.isVoidStop || xt.stepIndex >= renderSteps.length) { - const r = context.output.close(); - xt.resolve(r); - return null; - } else { - // TODO: support soft stop - const step = renderSteps[xt.stepIndex++]; - const tk = step.tk; - const withId = step.insertTokenId; - switch (step.code) { - case STEP_HANDLER: - if (withId) insertTokenId(tk); - return context.handleTokenResult(tk.id, tk[TOKEN_HANDLER](context, tk), e => { - if (withId) insertTokenIdEnd(tk); - return renderNext(e, xt); - }); - case STEP_STR_TOKEN: - context.output.add(tk.str); - break; - case STEP_NO_HANDLER: - context.output.add(``); - break; - case STEP_LITERAL_HANDLER: - if (withId) insertTokenId(tk); - context.output.add(step.data); - if (withId) insertTokenIdEnd(tk); - break; - } - return renderNext(null, xt); - } -} - -function executeRenderSteps(renderSteps, context) { - return new Promise(resolve => { - const xt = { stepIndex: 0, renderSteps, context, resolve }; - return renderNext(null, xt); - }); -} - -module.exports = { executeRenderSteps, renderNext, executeSteps }; diff --git a/packages/electrode-react-webapp/lib/render-output.js b/packages/electrode-react-webapp/lib/render-output.js deleted file mode 100644 index de5d0da61..000000000 --- a/packages/electrode-react-webapp/lib/render-output.js +++ /dev/null @@ -1,231 +0,0 @@ -"use strict"; - -const assert = require("assert"); - -class Output { - constructor() { - this._items = []; - } - - add(data) { - const x = this._items.length; - this._items.push(data); - return x; - } - - get length() { - return this._items.length; - } - - stringify() { - let out = ""; - for (const x of this._items) { - if (typeof x === "string") { - out += x; - } else if (x && x.stringify) { - out += x.stringify(); - } else { - const typeName = (x && x.constructor && x.constructor.name) || typeof x; - const msg = `RenderOutput unable to stringify item of type ${typeName}`; - console.error("FATAL Error:", msg + "\n"); // eslint-disable-line - throw new Error(msg); - } - } - return out; - } - - _munchItems(munchy) { - for (const item of this._items) { - if (item._munchItems) { - item._munchItems(munchy); - } else { - munchy.munch(item); - } - } - } - - sendToMunchy(munchy, done) { - if (this._items.length > 0) { - munchy.once("munched", done); - this._munchItems(munchy); - } else { - process.nextTick(done); - } - } -} - -class SpotOutput extends Output { - constructor() { - super(); - this._open = true; - this._pos = -1; - this._closeCb = null; - } - - add(data) { - assert(this._open, "SpotOutput closed"); - assert( - typeof data === "string" || Buffer.isBuffer(data) || (data && data._readableState), - "Must add only string/buffer/stream to SpotOutput" - ); - return super.add(data); - } - - close() { - assert(this._open, "closing already closed SpotOutput"); - this._open = false; - this._closeCb(); - } - - _onClose(cb) { - this._closeCb = cb; - } - - _spotPos() { - return this._pos; - } - - _setPos(x) { - this._pos = x; - } -} - -class MainOutput extends Output { - constructor() { - super(); - this._pending = 0; - } - - _addSpot(data) { - const x = this.add(data); - this._pending++; - return x; - } - - _closeSpot(x) { - assert(this._items[x._spotPos()] === x, "closing unknown pending"); - this._pending--; - } - - _hasPending() { - return this._pending > 0; - } -} - -class RenderOutput { - constructor(context) { - this._output = new MainOutput(); - this._flushQ = []; - this._context = context || { transform: x => x }; - this._result = ""; // will hold final result if context doesn't have send - } - - // add data to the end of the output - add(data) { - this._output.add(data); - } - - // reserve current end of output as an async fixed spot so data can be appended - // at that point when they become available async, even if more data has been - // added to the end of the output after this. - reserve() { - let output = this._output; - const spot = new SpotOutput(); - spot._setPos(output._addSpot(spot)); - spot._onClose(() => { - output._closeSpot(spot); - output = undefined; - this._checkFlushQ(); - }); - return spot; - } - - // flush data so far - flush() { - if (this._output && this._output.length > 0) { - this._flushQ.push(this._output); - if (!this._end) { - this._output = new MainOutput(); - } else { - this._output = undefined; - } - } - - this._checkFlushQ(); - } - - // close the output and returns a promise that waits for all pending - // spots to close and resolves with the final result - close() { - this._end = true; - - if (this._context.munchy) { - this.flush(); - // streaming - return this._context.transform(this._context.munchy, this); - } else { - const promise = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - this.flush(); - return promise; - } - } - - _finish() { - try { - if (this._context.munchy) { - // terminates munchy stream - this._result = this._context.munchy; - this._context.munchy.munch(null); - } - - if (this._resolve) { - this._resolve(this._context.transform(this._result, this)); - } - } catch (error) { - if (this._reject) { - this._reject(error); - } else { - throw error; - } - } - - this._resolve = this._reject = undefined; - } - - _checkFlushQ() { - if (this._flushQ.length < 1) { - if (this._end) { - this._finish(); - } - return; - } - - const output = this._flushQ[0]; - - if (!output._hasPending()) { - this._flushQ.shift(); - // handle streaming - // if this._context.munchy stream exist, then pipe output to it. - - if (this._context.munchy) { - // send output to munchy stream - output.sendToMunchy(this._context.munchy, () => this._checkFlushQ()); - } else { - const x = output.stringify(); - - if (this._context.send) { - this._context.send(x); - } else { - this._result += x; - } - - this._checkFlushQ(); - } - } - } -} - -module.exports = RenderOutput; diff --git a/packages/electrode-react-webapp/lib/renderer.js b/packages/electrode-react-webapp/lib/renderer.js deleted file mode 100644 index d76bbb809..000000000 --- a/packages/electrode-react-webapp/lib/renderer.js +++ /dev/null @@ -1,85 +0,0 @@ -"use strict"; - -const { executeSteps, executeRenderSteps } = require("./render-execute"); - -const { STEP_HANDLER, STEP_STR_TOKEN, STEP_NO_HANDLER, STEP_LITERAL_HANDLER } = executeSteps; - -class Renderer { - constructor(options) { - const insertTokenIds = Boolean(options.insertTokenIds); - // the last handler wins if it contains a token - const tokenHandlers = options.tokenHandlers.reverse(); - - const makeNullRemovedStep = (tk, cause) => { - return { - tk, - insertTokenId: false, - code: STEP_LITERAL_HANDLER, - data: `\n` - }; - }; - - const makeHandlerStep = tk => { - // look for first handler that has a token function for tk.id - const handler = tokenHandlers.find(h => h.tokens.hasOwnProperty(tk.id)); - - // no handler has function for token - if (!handler) { - const msg = `electrode-react-webapp: no handler found for token id ${tk.id}`; - console.error(msg); // eslint-disable-line - return { tk, code: STEP_NO_HANDLER }; - } - - const tkFunc = handler.tokens[tk.id]; - - if (tkFunc === null) { - if (insertTokenIds) return makeNullRemovedStep(tk, "handler set to null"); - - return null; - } - - if (typeof tkFunc !== "function") { - // not a function, just add it to output - return { - tk, - code: STEP_LITERAL_HANDLER, - insertTokenId: insertTokenIds && !tk.props._noInsertId, - data: tkFunc - }; - } - - tk.setHandler(tkFunc); - - return { tk, code: STEP_HANDLER, insertTokenId: insertTokenIds && !tk.props._noInsertId }; - }; - - const makeStep = tk => { - // token is a literal string, just add it to output - if (tk.hasOwnProperty("str")) { - return { tk, code: STEP_STR_TOKEN }; - } - - // token is not pointing to a module, so lookup from token handlers - if (!tk.isModule) return makeHandlerStep(tk); - - if (tk.custom === null) { - if (insertTokenIds) return makeNullRemovedStep(tk, "process return null"); - return null; - } - - return { - tk, - code: STEP_HANDLER, - insertTokenId: options.insertTokenIds && !tk.props._noInsertId - }; - }; - - this.renderSteps = options.htmlTokens.map(makeStep).filter(x => x); - } - - render(context) { - return executeRenderSteps(this.renderSteps, context); - } -} - -module.exports = Renderer; diff --git a/packages/electrode-react-webapp/lib/symbols.js b/packages/electrode-react-webapp/lib/symbols.js deleted file mode 100644 index 23258f817..000000000 --- a/packages/electrode-react-webapp/lib/symbols.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; - -module.exports = { - TEMPLATE_DIR: Symbol("template dir"), - TOKEN_HANDLER: Symbol("token handler") -}; diff --git a/packages/electrode-react-webapp/lib/token.js b/packages/electrode-react-webapp/lib/token.js deleted file mode 100644 index 08d43f258..000000000 --- a/packages/electrode-react-webapp/lib/token.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, no-console, max-params, max-statements */ - -const assert = require("assert"); -const loadHandler = require("./load-handler"); -const { TEMPLATE_DIR, TOKEN_HANDLER } = require("./symbols"); - -const viewTokenModules = {}; - -class Token { - constructor(id, pos, props, templateDir) { - this.id = id; - - // match `require(path/to/module)` - const match = id.match(/^require\(['"]?([^'"\)]+)['"]?\)/); - if (match) { - this.modPath = match[1]; - this.isModule = true; - } else if (id.startsWith("#")) { - this.modPath = this.id.substr(1); // remove the leading # - this.isModule = true; - } else { - this.isModule = false; - } - - this.pos = pos; - this.custom = undefined; - this.wantsNext = undefined; - this.props = props || {}; - if (this.props._call) { - this._modCall = [].concat(this.props._call); - } - this[TOKEN_HANDLER] = null; - this[TEMPLATE_DIR] = this.props[TEMPLATE_DIR] || templateDir || process.cwd(); - } - - // if token is a module, then load it - load(options) { - if (!this.isModule || this.custom !== undefined) return; - - let tokenMod = viewTokenModules[this.id]; - - if (tokenMod === undefined) { - tokenMod = loadHandler(this.modPath, this[TEMPLATE_DIR], this._modCall && this._modCall[0]); - viewTokenModules[this.id] = tokenMod; - } - - if (this._modCall) { - // call setup function to get an instance - const params = [options || {}, this].concat(this._modCall[1] || []); - assert( - tokenMod[this._modCall[0]], - `electrode-react-webapp: _call of token ${this.id} - '${this._modCall[0]}' not found` - ); - this.custom = tokenMod[this._modCall[0]](...params); - } else { - this.custom = tokenMod(options || {}, this); - } - - if (this.custom === null) return; - - assert( - this.custom && this.custom.process, - `custom token ${this.id} module doesn't have process method` - ); - - // if process function takes more than one params, then it should take a - // next callback so it can do async work, and call next after that's done. - this.wantsNext = this.custom.process.length > 1; - this.setHandler(context => this.custom.process(context, this)); - } - - setHandler(func) { - this[TOKEN_HANDLER] = func; - } -} - -module.exports = Token; diff --git a/packages/electrode-react-webapp/lib/utils.js b/packages/electrode-react-webapp/lib/utils.js deleted file mode 100644 index 96adc9772..000000000 --- a/packages/electrode-react-webapp/lib/utils.js +++ /dev/null @@ -1,339 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers, no-console */ - -const _ = require("lodash"); -const fs = require("fs"); -const Path = require("path"); -const requireAt = require("require-at"); -const assert = require("assert"); -const Url = require("url"); - -const HTTP_PORT = "80"; - -/** - * Tries to import bundle chunk selector function if the corresponding option is set in the - * webapp plugin configuration. The function takes a `request` object as an argument and - * returns the chunk name. - * - * @param {Object} options - webapp plugin configuration options - * @return {Function} function that selects the bundle based on the request object - */ -function resolveChunkSelector(options) { - if (options.bundleChunkSelector) { - return require(Path.resolve(options.bundleChunkSelector)); // eslint-disable-line - } - - return () => ({ - css: "main", - js: "main" - }); -} - -/** - * Load stats.json which is created during build. - * Attempt to load the stats.json file which contains a manifest of - * The file contains bundle files which are to be loaded on the client side. - * - * @param {string} statsPath - path of stats.json - * @returns {Promise.} an object containing an array of file names - */ -function loadAssetsFromStats(statsPath) { - let stats; - try { - stats = JSON.parse(fs.readFileSync(Path.resolve(statsPath)).toString()); - } catch (err) { - return {}; - } - const assets = {}; - const manifestAsset = _.find(stats.assets, asset => { - return asset.name.endsWith("manifest.json"); - }); - const jsAssets = stats.assets.filter(asset => { - return asset.name.endsWith(".js"); - }); - const cssAssets = stats.assets.filter(asset => { - return asset.name.endsWith(".css"); - }); - - if (manifestAsset) { - assets.manifest = manifestAsset.name; - } - - assets.js = jsAssets; - assets.css = cssAssets; - - return assets; -} - -function getIconStats(iconStatsPath) { - let iconStats; - try { - iconStats = fs.readFileSync(Path.resolve(iconStatsPath)).toString(); - iconStats = JSON.parse(iconStats); - } catch (err) { - return ""; - } - if (iconStats && iconStats.html) { - return iconStats.html.join(""); - } - return iconStats; -} - -function getCriticalCSS(path) { - const criticalCSSPath = Path.resolve(process.cwd(), path || ""); - - try { - const criticalCSS = fs.readFileSync(criticalCSSPath).toString(); - return criticalCSS; - } catch (err) { - return ""; - } -} - -/** - * Resolves the path to the stats.json file containing our - * asset list. In dev the stats.json file is written to a - * build artifacts directory, while in produciton its contained - * within the dist/server folder - * @param {string} statsFilePath path to stats.json - * @param {string} buildArtifactsPath path to stats.json in dev - * @return {string} current active path - */ -function getStatsPath(statsFilePath, buildArtifactsPath) { - return process.env.WEBPACK_DEV === "true" - ? Path.resolve(buildArtifactsPath, "stats.json") - : statsFilePath; -} - -function htmlifyError(err, withStack) { - const html = err.html ? `
${err.html}
\n` : ""; - const errMsg = () => { - if (withStack !== false && err.stack) { - if (process.env.NODE_ENV !== "production") { - const rgx = new RegExp(process.cwd(), "g"); - return err.stack.replace(rgx, "CWD"); - } else { - return `- Not sending Error stack for production\n\nMessage: ${err.message}`; - } - } else { - return err.message; - } - }; - return `OOPS -${html} -
-${errMsg()}
-
`; -} - -function getDevCssBundle(chunkNames, routeData) { - const devBundleBase = routeData.devBundleBase; - if (chunkNames.css) { - const cssChunks = Array.isArray(chunkNames.css) ? chunkNames.css : [chunkNames.css]; - return _.map(cssChunks, chunkName => `${devBundleBase}${chunkName}.style.css`); - } else { - return [`${devBundleBase}style.css`]; - } -} - -function getDevJsBundle(chunkNames, routeData) { - const devBundleBase = routeData.devBundleBase; - - return chunkNames.js - ? `${devBundleBase}${chunkNames.js}.bundle.dev.js` - : `${devBundleBase}bundle.dev.js`; -} - -function getProdBundles(chunkNames, routeData) { - if (!routeData || !routeData.assets) { - return {}; - } - - const { assets } = routeData; - - return { - jsChunk: _.find(assets.js, asset => _.includes(asset.chunkNames, chunkNames.js)), - - cssChunk: _.filter(assets.css, asset => - _.some(asset.chunkNames, assetChunkName => _.includes(chunkNames.css, assetChunkName)) - ) - }; -} - -function processRenderSsMode(request, renderSs, mode) { - if (renderSs) { - if (mode === "noss") { - return false; - } else if (renderSs === "datass" || mode === "datass") { - renderSs = "datass"; - // signal user content callback to populate prefetch data only and skips actual SSR - _.set(request, ["app", "ssrPrefetchOnly"], true); - } - } - - return renderSs; -} - -function getCspNonce(request, cspNonceValue) { - let scriptNonce = ""; - let styleNonce = ""; - - if (cspNonceValue) { - if (typeof cspNonceValue === "function") { - scriptNonce = cspNonceValue(request, "script"); - styleNonce = cspNonceValue(request, "style"); - } else { - scriptNonce = _.get(request, cspNonceValue.script); - styleNonce = _.get(request, cspNonceValue.style); - } - scriptNonce = scriptNonce ? ` nonce="${scriptNonce}"` : ""; - styleNonce = styleNonce ? ` nonce="${styleNonce}"` : ""; - } - - return { scriptNonce, styleNonce }; -} - -const resolvePath = path => (!Path.isAbsolute(path) ? Path.resolve(path) : path); - -function responseForError(request, routeOptions, err) { - return { - status: err.status || 500, - html: htmlifyError(err, routeOptions.replyErrorStack) - }; -} - -function responseForBadStatus(request, routeOptions, data) { - return { - status: data.status, - html: (data && data.html) || data - }; -} - -function loadFuncFromModule(modulePath, exportFuncName, requireAtDir) { - const mod = requireAt(requireAtDir || process.cwd())(modulePath); - const exportFunc = (exportFuncName && mod[exportFuncName]) || mod; - assert( - typeof exportFunc === "function", - `loadFuncFromModule ${modulePath} doesn't export a usable function` - ); - return exportFunc; -} - -function invokeTemplateProcessor(asyncTemplate, routeOptions) { - const tp = routeOptions.templateProcessor; - - if (tp) { - let tpFunc; - if (typeof tp === "string") { - tpFunc = loadFuncFromModule(tp, "templateProcessor"); - } else { - tpFunc = tp; - assert(typeof tpFunc === "function", `templateProcessor is not a function`); - } - - return tpFunc(asyncTemplate, routeOptions); - } - - return undefined; -} - -function getOtherStats() { - const otherStats = {}; - if (fs.existsSync("dist/server")) { - fs.readdirSync("dist/server") - .filter(x => x.endsWith("-stats.json")) - .reduce((prev, x) => { - const k = Path.basename(x).split("-")[0]; - prev[k] = `dist/server/${x}`; - return prev; - }, otherStats); - } - return otherStats; -} - -function getOtherAssets(pluginOptions) { - return Object.entries(pluginOptions.otherStats).reduce((prev, [k, v]) => { - prev[k] = loadAssetsFromStats(getStatsPath(v, pluginOptions.buildArtifacts)); - return prev; - }, {}); -} - -function getBundleJsNameByQuery(data, otherAssets) { - let { name } = data.jsChunk; - const { __dist } = data.query; - if (__dist && otherAssets[__dist]) { - name = `${__dist}${name.substr(name.indexOf("."))}`; - } - return name; -} - -const munchyHandleStreamError = err => { - let errMsg = (process.env.NODE_ENV !== "production" && err.stack) || err.message; - - if (process.cwd().length > 3) { - errMsg = (errMsg || "").replace(new RegExp(process.cwd(), "g"), "CWD"); - } - - return { - result: ` -

SSR ERROR

-${errMsg}
-

`, - remit: false - }; -}; - -const makeDevBundleBase = devServer => { - const cdnProtocol = process.env.WEBPACK_DEV_CDN_PROTOCOL; - const cdnHostname = process.env.WEBPACK_DEV_CDN_HOSTNAME; - const cdnPort = process.env.WEBPACK_DEV_CDN_PORT; - - /* - * If env specified custom CDN protocol/host/port, then generate bundle - * URL with those. - */ - if (cdnProtocol !== undefined || cdnHostname !== undefined || cdnPort !== undefined) { - return Url.format({ - protocol: cdnProtocol || "http", - hostname: cdnHostname || "localhost", - // if CDN is also from standard HTTP port 80 then it's not needed in the URL - port: cdnPort !== HTTP_PORT ? cdnPort : "", - pathname: "/js/" - }); - } else if (process.env.APP_SERVER_PORT) { - return "/js/"; - } else { - return Url.format({ - protocol: devServer.protocol, - hostname: devServer.host, - port: devServer.port, - pathname: "/js/" - }); - } -}; - -module.exports = { - resolveChunkSelector, - loadAssetsFromStats, - getIconStats, - getCriticalCSS, - getStatsPath, - resolvePath, - htmlifyError, - getDevCssBundle, - getDevJsBundle, - getProdBundles, - processRenderSsMode, - getCspNonce, - responseForError, - responseForBadStatus, - loadFuncFromModule, - invokeTemplateProcessor, - getOtherStats, - getOtherAssets, - getBundleJsNameByQuery, - isReadableStream: x => Boolean(x && x.pipe && x.on && x._readableState), - munchyHandleStreamError, - makeDevBundleBase -}; diff --git a/packages/electrode-react-webapp/package.json b/packages/electrode-react-webapp/package.json deleted file mode 100644 index 39e24ffd4..000000000 --- a/packages/electrode-react-webapp/package.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "name": "electrode-react-webapp", - "version": "5.1.1", - "description": "Hapi plugin that provides a default React web app template", - "main": "index.js", - "scripts": { - "build": "echo \"Nothing to build. Just a placeholder\"", - "lint": "clap lint", - "pre-test": "clap compile", - "prepare": "clap compile", - "test": "clap test-only", - "coverage": "clap test-cov", - "check": "clap check", - "prepublishOnly": "clap check", - "format": "prettier --write --print-width 100 *.{js,jsx} `find . -type d -d 1 -exec echo '{}/**/*.{js,jsx}' \\; | egrep -v '(/node_modules/|/dist/|/coverage/)'`" - }, - "xclap": { - "tasks": { - "compile": "babel src-template -D -d template", - "test-only": "~[compile, electrode/test-only]", - "check": "~[compile, electrode/check]" - } - }, - "homepage": "http://www.electrode.io", - "repository": { - "type": "git", - "url": "https://github.com/electrode-io/electrode.git" - }, - "bugs": { - "url": "https://github.com/electrode-io/electrode/issues" - }, - "keywords": [ - "electrode", - "node", - "webserver" - ], - "author": "Electrode (http://www.electrode.io/)", - "contributors": [ - { - "name": "Joel Chen", - "email": "xchen@walmartlabs.com" - }, - { - "name": "Caoyang Shi", - "email": "cshi@walmartlabs.com" - }, - { - "name": "Arpan Nanavati", - "email": "nanavatiarpan@gmail.com" - } - ], - "license": "Apache-2.0", - "files": [ - "lib", - "template", - "index.js" - ], - "dependencies": { - "electrode-archetype-njs-module-dev": "^3.0.3", - "electrode-hapi-compat": "^1.2.0", - "eslint": "^7.32.0", - "eslint-config-walmart": "^2.2.1", - "eslint-plugin-filenames": "^1.1.0", - "eslint-plugin-jsdoc": "^30.7.9", - "http-status-codes": "^1.3.0", - "in-publish": "^2.0.0", - "lodash": "^4.17.10", - "munchy": "^1.0.9", - "require-at": "^1.0.6", - "string-array": "^1.0.0", - "xaa": "^1.6.0" - }, - "devDependencies": { - "@babel/cli": "^7.22.10", - "@babel/core": "^7.22.11", - "@babel/preset-env": "^7.22.10", - "@babel/preset-react": "^7.22.5", - "@babel/register": "^7.22.5", - "@xarc/module-dev": "^4.1.0", - "@xarc/run": "^1.1.1", - "babel-eslint": "^10.1.0", - "benchmark": "^2.1.4", - "chai": "4.3.6", - "electrode-redux-router-engine": "6.0.0", - "electrode-server": "^1.8.0", - "electrode-server2": "npm:electrode-server@^2.0.0", - "eslint": "^7.32.0", - "eslint-config-walmart": "^2.2.1", - "eslint-plugin-filenames": "^1.1.0", - "eslint-plugin-jsdoc": "^30.7.9", - "express": "^4.15.4", - "koa": "^2.3.0", - "koa-router": "^7.4.0", - "mocha": "^10.2.0", - "nyc": "^15.1.0", - "object-assign": "^4.1.0", - "prettier": "^1.5.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-helmet": "^5.2.0", - "run-verify": "^1.0.2", - "stream-to-array": "^2.3.0", - "superagent": "^1.8.5", - "uglify-js": "^2.6.2", - "xclap": "^0.2.0", - "xstdout": "^0.1.1" - }, - "nyc": { - "all": true, - "check-coverage": true, - "statements": 100, - "branches": 98, - "functions": 100, - "lines": 100, - "cache": true, - "reporter": [ - "lcov", - "text", - "text-summary" - ], - "exclude": [ - "coverage", - "*clap.js", - "gulpfile.js", - "dist", - "test", - "src-template", - "electrode-server2" - ], - "extension": [ - ".jsx" - ], - "require": [ - "@babel/register" - ] - }, - "@xarc/module-dev": { - "srcDir": [ - "src", - "test", - "scripts" - ], - "features": [ - "eslint", - "eslintTS", - "mocha", - "typedoc", - "typescript" - ] - }, - "prettier": { - "printWidth": 100, - "trailingComma": "none", - "arrowParens": "avoid" - } -} diff --git a/packages/electrode-react-webapp/src-template/.babelrc b/packages/electrode-react-webapp/src-template/.babelrc deleted file mode 100644 index 24b086f24..000000000 --- a/packages/electrode-react-webapp/src-template/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "8" - } - } - ], - "@babel/preset-react" - ] -} diff --git a/packages/electrode-react-webapp/src-template/index.jsx b/packages/electrode-react-webapp/src-template/index.jsx deleted file mode 100644 index da2f06df7..000000000 --- a/packages/electrode-react-webapp/src-template/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -/* @jsx createElement */ - -const { IndexPage, createElement, Token } = require("../lib/jsx"); - -const Template = ( - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - -
-); - -export default Template; diff --git a/packages/electrode-react-webapp/test/.eslintrc b/packages/electrode-react-webapp/test/.eslintrc deleted file mode 100644 index a4edd1e02..000000000 --- a/packages/electrode-react-webapp/test/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ ---- -extends: - - "../node_modules/electrode-archetype-njs-module-dev/config/eslint/.eslintrc-test" diff --git a/packages/electrode-react-webapp/test/data/bad-content.js b/packages/electrode-react-webapp/test/data/bad-content.js deleted file mode 100644 index 2bc93b984..000000000 --- a/packages/electrode-react-webapp/test/data/bad-content.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ - -require("foo-blah"); diff --git a/packages/electrode-react-webapp/test/data/chunk-selector.js b/packages/electrode-react-webapp/test/data/chunk-selector.js deleted file mode 100644 index fe9a3b56c..000000000 --- a/packages/electrode-react-webapp/test/data/chunk-selector.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; - -const FOO_BUNDLE = { - css: "foo", - js: "foo" -}; -const BAR_BUNDLE = { - css: "bar", - js: "bar" -}; -const DEFAULT_BUNDLE = { - css: "home", - js: "home" -}; -const MULTI_BUNDLE = { - css: ["foo", "bar"], - js: "home" -}; - -module.exports = request => { - if (request.path.endsWith("/foo")) { - return FOO_BUNDLE; - } else if (request.path.endsWith("/bar")) { - return BAR_BUNDLE; - } else if (request.path.endsWith("/multi-chunk")) { - return MULTI_BUNDLE; - } else if (request.path.endsWith("/empty")) { - return {}; - } - - return DEFAULT_BUNDLE; -}; diff --git a/packages/electrode-react-webapp/test/data/critical.css b/packages/electrode-react-webapp/test/data/critical.css deleted file mode 100644 index 892782617..000000000 --- a/packages/electrode-react-webapp/test/data/critical.css +++ /dev/null @@ -1 +0,0 @@ -body {color: green;} diff --git a/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.dev.json b/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.dev.json deleted file mode 100644 index 44442878f..000000000 --- a/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.dev.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "production": false -} diff --git a/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.json b/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.json deleted file mode 100644 index 2da76016b..000000000 --- a/packages/electrode-react-webapp/test/data/dist/electrode-dll-assets.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "production": true -} diff --git a/packages/electrode-react-webapp/test/data/foo.js b/packages/electrode-react-webapp/test/data/foo.js deleted file mode 100644 index 80e295b96..000000000 --- a/packages/electrode-react-webapp/test/data/foo.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "hello"; diff --git a/packages/electrode-react-webapp/test/data/icon-stats-empty.json b/packages/electrode-react-webapp/test/data/icon-stats-empty.json deleted file mode 100644 index 6d6f06a49..000000000 --- a/packages/electrode-react-webapp/test/data/icon-stats-empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "empty": true -} diff --git a/packages/electrode-react-webapp/test/data/icon-stats-test-pwa.json b/packages/electrode-react-webapp/test/data/icon-stats-test-pwa.json deleted file mode 100644 index 1e2974dc3..000000000 --- a/packages/electrode-react-webapp/test/data/icon-stats-test-pwa.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "html": [ - "", - "", - "" - ] -} diff --git a/packages/electrode-react-webapp/test/data/index-1.html b/packages/electrode-react-webapp/test/data/index-1.html deleted file mode 100644 index 25061af8c..000000000 --- a/packages/electrode-react-webapp/test/data/index-1.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - -
test html-1
- -
- - -
- - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/index-2.html b/packages/electrode-react-webapp/test/data/index-2.html deleted file mode 100644 index ccac1bb1f..000000000 --- a/packages/electrode-react-webapp/test/data/index-2.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - -
test html-2
-
- -
- - - - - - - diff --git a/packages/electrode-react-webapp/test/data/perf-1.html b/packages/electrode-react-webapp/test/data/perf-1.html deleted file mode 100644 index 7044a06d7..000000000 --- a/packages/electrode-react-webapp/test/data/perf-1.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - -
test perf 1 html
- -
- -
- - - - - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/stats-test-multibundle.json b/packages/electrode-react-webapp/test/data/stats-test-multibundle.json deleted file mode 100644 index b0c3b1bc9..000000000 --- a/packages/electrode-react-webapp/test/data/stats-test-multibundle.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "assetsByChunkName": { - "foo": [ - "foo.bundle.f07a873ce87fc904a6a5.js", - "foo.style.f07a873ce87fc904a6a5.css" - ], - "bar": [ - "bar.bundle.f07a873ce87fc904a6a5.js", - "bar.style.f07a873ce87fc904a6a5.css" - ], - "home": [ - "home.bundle.f07a873ce87fc904a6a5.js", - "home.style.f07a873ce87fc904a6a5.css" - ] - }, - "assets": [{ - "name": "foo.style.f07a873ce87fc904a6a5.css", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "foo" - ] - }, { - "name": "foo.bundle.f07a873ce87fc904a6a5.js", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "foo" - ] - }, { - "name": "bar.style.f07a873ce87fc904a6a5.css", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "bar" - ] - }, { - "name": "bar.bundle.f07a873ce87fc904a6a5.js", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "bar" - ] - }, { - "name": "home.style.f07a873ce87fc904a6a5.css", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "home" - ] - }, { - "name": "home.bundle.f07a873ce87fc904a6a5.js", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "home" - ] - }] -} diff --git a/packages/electrode-react-webapp/test/data/stats-test-one-bundle.json b/packages/electrode-react-webapp/test/data/stats-test-one-bundle.json deleted file mode 100644 index 42edae47e..000000000 --- a/packages/electrode-react-webapp/test/data/stats-test-one-bundle.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assetsByChunkName": { - "bundle": [ - "bundle.f07a873ce87fc904a6a5.js", - "style.f07a873ce87fc904a6a5.css" - ] - }, - "assets": [{ - "name": "style.f07a873ce87fc904a6a5.css", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "main" - ] - }, { - "name": "bundle.f07a873ce87fc904a6a5.js", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "main" - ] - }] -} diff --git a/packages/electrode-react-webapp/test/data/stats-test-pwa.json b/packages/electrode-react-webapp/test/data/stats-test-pwa.json deleted file mode 100644 index e937fef13..000000000 --- a/packages/electrode-react-webapp/test/data/stats-test-pwa.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "assetsByChunkName": { - "bundle": [ - "bundle.f07a873ce87fc904a6a5.js", - "style.f07a873ce87fc904a6a5.css" - ], - "bar": [ - "bar.bundle.f07a873ce87fc904a6a5.js", - "bar.style.f07a873ce87fc904a6a5.css" - ] - }, - "assets": [{ - "name": "style.f07a873ce87fc904a6a5.css", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "bundle" - ] - }, { - "name": "bundle.f07a873ce87fc904a6a5.js", - "size": 888, - "chunks": [ - 0 - ], - "chunkNames": [ - "bundle" - ] - }, { - "name": "manifest.json", - "size": 888, - "chunks": [], - "chunkNames": [] - }] -} diff --git a/packages/electrode-react-webapp/test/data/style2.css b/packages/electrode-react-webapp/test/data/style2.css deleted file mode 100644 index 6446ebfd4..000000000 --- a/packages/electrode-react-webapp/test/data/style2.css +++ /dev/null @@ -1,3 +0,0 @@ -.test { - color: red; -} diff --git a/packages/electrode-react-webapp/test/data/template1.html b/packages/electrode-react-webapp/test/data/template1.html deleted file mode 100644 index f2d6bb644..000000000 --- a/packages/electrode-react-webapp/test/data/template1.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template2.html b/packages/electrode-react-webapp/test/data/template2.html deleted file mode 100644 index 5de95e820..000000000 --- a/packages/electrode-react-webapp/test/data/template2.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template3.html b/packages/electrode-react-webapp/test/data/template3.html deleted file mode 100644 index 33c0be87c..000000000 --- a/packages/electrode-react-webapp/test/data/template3.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template4.html b/packages/electrode-react-webapp/test/data/template4.html deleted file mode 100644 index adfea1d08..000000000 --- a/packages/electrode-react-webapp/test/data/template4.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template5.html b/packages/electrode-react-webapp/test/data/template5.html deleted file mode 100644 index 7856e3bdc..000000000 --- a/packages/electrode-react-webapp/test/data/template5.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template6.html b/packages/electrode-react-webapp/test/data/template6.html deleted file mode 100644 index 72f9c17e9..000000000 --- a/packages/electrode-react-webapp/test/data/template6.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/packages/electrode-react-webapp/test/data/template7.html b/packages/electrode-react-webapp/test/data/template7.html deleted file mode 100644 index f08f0f36e..000000000 --- a/packages/electrode-react-webapp/test/data/template7.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/packages/electrode-react-webapp/test/data/template8.html b/packages/electrode-react-webapp/test/data/template8.html deleted file mode 100644 index 60e9a22eb..000000000 --- a/packages/electrode-react-webapp/test/data/template8.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/packages/electrode-react-webapp/test/data/test-content.js b/packages/electrode-react-webapp/test/data/test-content.js deleted file mode 100644 index ae795d49f..000000000 --- a/packages/electrode-react-webapp/test/data/test-content.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - "status": 200, - "html": "
Test1 Electrode
", - "prefetch": "console.log('test1');" -}; diff --git a/packages/electrode-react-webapp/test/data/test-content17.js b/packages/electrode-react-webapp/test/data/test-content17.js deleted file mode 100644 index 463551e68..000000000 --- a/packages/electrode-react-webapp/test/data/test-content17.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - "status": 200, - "html": "
Test1 Electrode17
", - "prefetch": "console.log('test1');" -}; diff --git a/packages/electrode-react-webapp/test/data/test-dll-assets.json b/packages/electrode-react-webapp/test/data/test-dll-assets.json deleted file mode 100644 index 03de32b27..000000000 --- a/packages/electrode-react-webapp/test/data/test-dll-assets.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "foo": { - "bar": [1] - } -} diff --git a/packages/electrode-react-webapp/test/fixtures/async-error.js b/packages/electrode-react-webapp/test/fixtures/async-error.js deleted file mode 100644 index a84cb4dc2..000000000 --- a/packages/electrode-react-webapp/test/fixtures/async-error.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - process: function(context) { - context.output.add("\nfrom async error module"); - return Promise.reject("error from test/fixtures/async-error"); - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/async-ok.js b/packages/electrode-react-webapp/test/fixtures/async-ok.js deleted file mode 100644 index 1d5db1518..000000000 --- a/packages/electrode-react-webapp/test/fixtures/async-ok.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - process: function(context) { - context.output.add("\nfrom async ok module"); - return Promise.resolve(); - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/custom-1.js b/packages/electrode-react-webapp/test/fixtures/custom-1.js deleted file mode 100644 index 91b977aa4..000000000 --- a/packages/electrode-react-webapp/test/fixtures/custom-1.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -module.exports = function setup() { - return { - name: "custom-1", - process: function(context) { - context.output.add("
from custom-1
"); - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/custom-call.js b/packages/electrode-react-webapp/test/fixtures/custom-call.js deleted file mode 100644 index d9e636021..000000000 --- a/packages/electrode-react-webapp/test/fixtures/custom-call.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -function setup() { - return { - name: "custom-call", - process: function() { - return Promise.resolve(`_call process from custom-call token fixture`); - } - }; -} - -module.exports = { - setup -}; diff --git a/packages/electrode-react-webapp/test/fixtures/custom-count.js b/packages/electrode-react-webapp/test/fixtures/custom-count.js deleted file mode 100644 index d8f08698a..000000000 --- a/packages/electrode-react-webapp/test/fixtures/custom-count.js +++ /dev/null @@ -1,7 +0,0 @@ -let count = 0; -module.exports = () => { - count++; - return { - process: () => `${count}` - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/custom-fail.js b/packages/electrode-react-webapp/test/fixtures/custom-fail.js deleted file mode 100644 index 771c2752e..000000000 --- a/packages/electrode-react-webapp/test/fixtures/custom-fail.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("fail"); diff --git a/packages/electrode-react-webapp/test/fixtures/custom-null.js b/packages/electrode-react-webapp/test/fixtures/custom-null.js deleted file mode 100644 index afb179f85..000000000 --- a/packages/electrode-react-webapp/test/fixtures/custom-null.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = () => { - return null; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/dynamic-index-1.html b/packages/electrode-react-webapp/test/fixtures/dynamic-index-1.html deleted file mode 100644 index 641884356..000000000 --- a/packages/electrode-react-webapp/test/fixtures/dynamic-index-1.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - DYNAMIC_INDEX_1 -
- -
- - - - - diff --git a/packages/electrode-react-webapp/test/fixtures/dynamic-index-2.html b/packages/electrode-react-webapp/test/fixtures/dynamic-index-2.html deleted file mode 100644 index 9d449da06..000000000 --- a/packages/electrode-react-webapp/test/fixtures/dynamic-index-2.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - DYNAMIC_INDEX_2 -
- -
- - - - - - diff --git a/packages/electrode-react-webapp/test/fixtures/non-render-error.js b/packages/electrode-react-webapp/test/fixtures/non-render-error.js deleted file mode 100644 index 004c071e1..000000000 --- a/packages/electrode-react-webapp/test/fixtures/non-render-error.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - name: "non-render-error", - beforeRender: () => { - throw new Error("error from test/fixtures/non-render-error"); - }, - tokens: {} - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/perf-1-handler.js b/packages/electrode-react-webapp/test/fixtures/perf-1-handler.js deleted file mode 100644 index ca41e548d..000000000 --- a/packages/electrode-react-webapp/test/fixtures/perf-1-handler.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - "user-token-1": () => { - return "
user-token-1
"; - }, - - "user-token-2": context => { - context.output.add("
user-token-2
"); - }, - - "user-spot-token": context => { - const spot = context.output.reserve(); - process.nextTick(() => { - spot.add("
user-spot-1;"); - spot.add("user-spot-2;"); - spot.add("user-spot-3
"); - spot.close(); - }); - }, - - "user-promise-token": context => { - context.output.add("
user-promise-token
"); - }, - - PAGE_TITLE: () => { - return "user-handler-title"; - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/react-helmet-handler.js b/packages/electrode-react-webapp/test/fixtures/react-helmet-handler.js deleted file mode 100644 index 6f0b28326..000000000 --- a/packages/electrode-react-webapp/test/fixtures/react-helmet-handler.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; - -const Helmet = require("react-helmet").Helmet; - -const emptyTitleRegex = /]*><\/title>/; - -module.exports = handlerContext => { - const routeOptions = handlerContext.user.routeOptions; - const iconStats = handlerContext.user.routeData.iconStats; - - return { - HEAD_INITIALIZE: context => { - context.user.helmet = Helmet.renderStatic(); - }, - - PAGE_TITLE: context => { - const helmet = context.user.helmet; - const helmetTitleScript = helmet.title.toString(); - const helmetTitleEmpty = helmetTitleScript.match(emptyTitleRegex); - - return helmetTitleEmpty ? `${routeOptions.pageTitle}` : helmetTitleScript; - }, - - REACT_HELMET_SCRIPTS: context => { - const scriptsFromHelmet = ["link", "style", "script", "noscript"] - .map(tagName => context.user.helmet[tagName].toString()) - .join(""); - return `${scriptsFromHelmet}`; - }, - - META_TAGS: context => { - return context.user.helmet.meta.toString() + iconStats; - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/return-undefined.js b/packages/electrode-react-webapp/test/fixtures/return-undefined.js deleted file mode 100644 index d1823ec85..000000000 --- a/packages/electrode-react-webapp/test/fixtures/return-undefined.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - process: function() {} - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/string-only.js b/packages/electrode-react-webapp/test/fixtures/string-only.js deleted file mode 100644 index 76a7a8aea..000000000 --- a/packages/electrode-react-webapp/test/fixtures/string-only.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - process: function() { - return "\nfrom string only module"; - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/template-processor-1.js b/packages/electrode-react-webapp/test/fixtures/template-processor-1.js deleted file mode 100644 index 15d6c3e46..000000000 --- a/packages/electrode-react-webapp/test/fixtures/template-processor-1.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = function() { - return "template-processor-1"; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/template-processor-2.js b/packages/electrode-react-webapp/test/fixtures/template-processor-2.js deleted file mode 100644 index 115948f57..000000000 --- a/packages/electrode-react-webapp/test/fixtures/template-processor-2.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - templateProcessor: function() { - return "template-processor-2"; - } -}; diff --git a/packages/electrode-react-webapp/test/fixtures/template-processor-3.js b/packages/electrode-react-webapp/test/fixtures/template-processor-3.js deleted file mode 100644 index eb785eb37..000000000 --- a/packages/electrode-react-webapp/test/fixtures/template-processor-3.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - oops: function() { - return "template-processor-3"; - } -}; diff --git a/packages/electrode-react-webapp/test/fixtures/test-render-context.html b/packages/electrode-react-webapp/test/fixtures/test-render-context.html deleted file mode 100644 index 51f62a08b..000000000 --- a/packages/electrode-react-webapp/test/fixtures/test-render-context.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/electrode-react-webapp/test/fixtures/token-handler.js b/packages/electrode-react-webapp/test/fixtures/token-handler.js deleted file mode 100644 index 464b94a25..000000000 --- a/packages/electrode-react-webapp/test/fixtures/token-handler.js +++ /dev/null @@ -1,50 +0,0 @@ -"use strict"; - -/* eslint-disable no-magic-numbers */ - -module.exports = () => { - return { - "user-token-1": () => { - return "
user-token-1
"; - }, - - "user-token-2": context => { - context.output.add("
user-token-2
"); - }, - - "user-spot-token": context => { - const spot = context.output.reserve(); - spot.add("
user-spot-1;"); - setTimeout(() => { - spot.add("user-spot-2;"); - setTimeout(() => { - spot.add("user-spot-3
"); - spot.close(); - }, 20); - }, 10); - }, - - "user-promise-token": context => { - return new Promise(resolve => { - setTimeout(() => { - context.output.add("
user-promise-token
"); - resolve(); - }, 10); - }); - }, - - "user-header-token": context => { - context.user.response.headers = { - "x-foo-bar": "hello-world" - }; - }, - - PAGE_TITLE: () => { - return "user-handler-title"; - }, - - TEST_DYNAMIC_2: () => { - return "RETURN_BY_TEST_DYANMIC_2"; - } - }; -}; diff --git a/packages/electrode-react-webapp/test/fixtures/token-mod-es6-default.js b/packages/electrode-react-webapp/test/fixtures/token-mod-es6-default.js deleted file mode 100644 index d7ccbd13c..000000000 --- a/packages/electrode-react-webapp/test/fixtures/token-mod-es6-default.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = function es6Default() {}; diff --git a/packages/electrode-react-webapp/test/fixtures/token-mod-handler.js b/packages/electrode-react-webapp/test/fixtures/token-mod-handler.js deleted file mode 100644 index 4ac2fcecb..000000000 --- a/packages/electrode-react-webapp/test/fixtures/token-mod-handler.js +++ /dev/null @@ -1 +0,0 @@ -exports.tokenHandler = function asTokenHandler() {}; diff --git a/packages/electrode-react-webapp/test/fixtures/token-mod-invalid.js b/packages/electrode-react-webapp/test/fixtures/token-mod-invalid.js deleted file mode 100644 index f8a020c54..000000000 --- a/packages/electrode-react-webapp/test/fixtures/token-mod-invalid.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "oops"; diff --git a/packages/electrode-react-webapp/test/fixtures/wants-next.js b/packages/electrode-react-webapp/test/fixtures/wants-next.js deleted file mode 100644 index 0449f4868..000000000 --- a/packages/electrode-react-webapp/test/fixtures/wants-next.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -module.exports = () => { - return { - process: function(context) { - context.output.add("\nfrom wants next module"); - } - }; -}; diff --git a/packages/electrode-react-webapp/test/jsx-templates/.babelrc b/packages/electrode-react-webapp/test/jsx-templates/.babelrc deleted file mode 100644 index 24b086f24..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "8" - } - } - ], - "@babel/preset-react" - ] -} diff --git a/packages/electrode-react-webapp/test/jsx-templates/index-1.jsx b/packages/electrode-react-webapp/test/jsx-templates/index-1.jsx deleted file mode 100644 index 287eb304f..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/index-1.jsx +++ /dev/null @@ -1,48 +0,0 @@ -/* @jsx createElement */ - -const { IndexPage, createElement, Token, Require } = require("../../lib/jsx"); - -const Template = ( - - - - - - - - - - - - - - - - - -
test html-1
- -
- - -
- - - - - - - - - - - - -
-); - -export default Template; diff --git a/packages/electrode-react-webapp/test/jsx-templates/index-2.jsx b/packages/electrode-react-webapp/test/jsx-templates/index-2.jsx deleted file mode 100644 index 9b42c1a3a..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/index-2.jsx +++ /dev/null @@ -1,42 +0,0 @@ -/* @jsx createElement */ - -const { IndexPage, createElement, Token } = require("../../lib/jsx"); - -const TestAsync = async () => { - return `
Hello from async tag JSX-2
`; -}; - -const Template = ( - - - - - - - - - - - - - - -
test jsx-2
-
- -
- - - - - - - -
-); - -export default Template; diff --git a/packages/electrode-react-webapp/test/jsx-templates/index-3.jsx b/packages/electrode-react-webapp/test/jsx-templates/index-3.jsx deleted file mode 100644 index bc78d0647..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/index-3.jsx +++ /dev/null @@ -1,25 +0,0 @@ -/* @jsx createElement */ - -const { IndexPage, createElement, Token, Require } = require("../../lib/jsx"); - -const Test = (props, context) => { - context.status = 204; - return `
Hello Test JSX-3
`; -}; - -const Template = ( - - - - - - - - - - - - -); - -export default Template; diff --git a/packages/electrode-react-webapp/test/jsx-templates/index-intercept.jsx b/packages/electrode-react-webapp/test/jsx-templates/index-intercept.jsx deleted file mode 100644 index a827a4a63..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/index-intercept.jsx +++ /dev/null @@ -1,28 +0,0 @@ -/* @jsx createElement */ - -const { IndexPage, createElement, Token, Require } = require("../../lib/jsx"); - -const Test = (props, context) => { - context.intercept({ - responseHandler: (request, h) => { - return h.response("context intercept handler").takeover(); - } - }); -}; - -const Template = ( - - - - - - - - - - - - -); - -export default Template; diff --git a/packages/electrode-react-webapp/test/jsx-templates/style.css b/packages/electrode-react-webapp/test/jsx-templates/style.css deleted file mode 100644 index 266cef311..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.test { - background: black; -} diff --git a/packages/electrode-react-webapp/test/jsx-templates/test-token.js b/packages/electrode-react-webapp/test/jsx-templates/test-token.js deleted file mode 100644 index da720f10d..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/test-token.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function setup(setupContext, token) { - const props = token.props; - - return { - process: () => props.name - }; -}; diff --git a/packages/electrode-react-webapp/test/jsx-templates/test1.jsx b/packages/electrode-react-webapp/test/jsx-templates/test1.jsx deleted file mode 100644 index 57db9e93f..000000000 --- a/packages/electrode-react-webapp/test/jsx-templates/test1.jsx +++ /dev/null @@ -1,140 +0,0 @@ -/* @jsx createElement */ - -import { IndexPage, createElement, Token, Require, Literal, Component } from "../../lib/jsx"; - -const getBogelFontUrl = () => { - return "bogel"; -}; - -const MyTest = (props, context) => { - return ( -
50}> - - my test -
- ); -}; - -class TestComponent1 extends Component { - constructor(props, context) { - super(props, context); - } - - render() { - return
from test component1{this.props.test1}
; - } -} - -function AsyncComponent(props, context, scope) { - return new Promise(resolve => { - setTimeout(() => { - scope.output.add(`${props.indent}async component ${props.key}\n`); - resolve(
async component children: {props.children}
); - }, props.delay); - }); -} - -function ChildrenNesting(props) { - return ( -
- component nesting children - {props.children} -
- ); -} - -const Template = () => ( - - - - - - - - - - - - - - - - ` }, - { - id: "meta-tags", - isModule: false, - pos: 206, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { - id: "#critical-css", - isModule: true, - modPath: "critical-css", - pos: 228, - custom: undefined, - wantsNext: false, - props: {} - }, - { str: "\n\n" } - ]; - const criticalCssToken = asyncTemplate.tokens[8]; - expect(criticalCssToken.custom.process()).to.equal( - `\ntoken process module critical-css not found\n` - ); - criticalCssToken.custom = undefined; - expect(asyncTemplate.tokens).to.deep.equal(expected); - }); - - const template2Expected = [ - { str: "\n\n" }, - { - id: "ssr-content", - isModule: false, - pos: 17, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { - id: "webapp-header-bundles", - isModule: false, - pos: 41, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { - id: "webapp-body-bundles", - isModule: false, - pos: 75, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { - id: "prefetch-bundles", - isModule: false, - pos: 107, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { str: `` }, - { - id: "webapp-body-bundles", - isModule: false, - pos: 183, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { - id: "meta-tags", - isModule: false, - pos: 215, - custom: undefined, - wantsNext: undefined, - props: {} - }, - { str: "\n\n" }, - { - id: "page-title", - isModule: false, - pos: 252, - custom: undefined, - wantsNext: undefined, - props: {} - } - ]; - - it("should parse template with token at end", () => { - const htmlFile = Path.join(__dirname, "../data/template2.html"); - intercept = xstdout.intercept(silentIntercept); - const asyncTemplate = new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }); - asyncTemplate.initializeRenderer(); - intercept.restore(); - - expect(asyncTemplate.tokens).to.deep.equal(template2Expected); - }); - - it("should parse template multi line tokens with props", () => { - const htmlFile = Path.join(__dirname, "../data/template3.html"); - - intercept = xstdout.intercept(silentIntercept); - - const asyncTemplate = new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }); - asyncTemplate.initializeRenderer(); - intercept.restore(); - - const expected = [ - { - str: "\n\n" - }, - { - id: "ssr-content", - isModule: false, - pos: 17, - props: { - attr: ["1", "2", "3"], - args: ["a", "b", "c"], - empty: "", - foo: "bar a [b] c", - hello: "world", - test: true - }, - custom: undefined, - wantsNext: undefined - }, - { - id: "prefetch-bundles", - isModule: false, - pos: 148, - props: {}, - custom: undefined, - wantsNext: undefined - }, - { - str: `" - }, - { - id: "meta-tags", - isModule: false, - pos: 264, - props: {}, - custom: undefined, - wantsNext: undefined - }, - - { - str: "\n\n" - }, - { - id: "page-title", - isModule: false, - pos: 301, - props: {}, - custom: undefined, - wantsNext: undefined - }, - { - custom: undefined, - id: "json-prop", - isModule: false, - pos: 326, - props: { - foo: "bar", - test: [1, 2, 3] - }, - wantsNext: undefined - }, - { - custom: undefined, - id: "space-tags", - isModule: false, - pos: 396, - props: {}, - wantsNext: undefined - }, - { - custom: undefined, - id: "new-line-tags", - isModule: false, - pos: 421, - props: {}, - wantsNext: undefined - }, - { - custom: undefined, - id: "space-newline-tag", - isModule: false, - pos: 456, - props: { - attr1: "hello", - attr2: "world", - attr3: "foo" - }, - wantsNext: undefined - }, - { - _modCall: ["setup"], - custom: { - name: "custom-call" - }, - id: `require("../fixtures/custom-call")`, - isModule: true, - modPath: "../fixtures/custom-call", - pos: 536, - props: { - _call: "setup" - }, - wantsNext: false - } - ]; - expect(typeof _.last(asyncTemplate.tokens).custom.process).to.equal("function"); - delete _.last(asyncTemplate.tokens).custom.process; - expect(asyncTemplate.tokens).to.deep.equal(expected); - }); - - it("should throw for token with invalid props", () => { - const htmlFile = Path.join(__dirname, "../data/template4.html"); - expect( - () => - new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }) - ).to.throw( - `at line 9 col 3 - 'prefetch-bundles bad-prop' has malformed prop: name must be name=Val;` - ); - }); - - it("should throw for token with invalid prop name", () => { - const htmlFile = Path.join(__dirname, "../data/template5.html"); - expect( - () => - new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }) - ).to.throw( - `at line 4 col 3 - 'ssr-content attr[1,2,3]' has malformed prop: name must be name=Val;` - ); - }); - - it("should throw for token empty body", () => { - const htmlFile = Path.join(__dirname, "../data/template7.html"); - expect( - () => - new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }) - ).to.throw(`at line 3 col 5 - empty token body`); - }); - - it("should throw for token missing close tag", () => { - const htmlFile = Path.join(__dirname, "../data/template8.html"); - expect( - () => - new AsyncTemplate({ - htmlFile, - tokenHandlers: "./test/fixtures/token-handler" - }) - ).to.throw(`at line 3 col 5 - Can't find token close tag for '`); - return server.close(() => resolve()); - }); - }); - }); - - it("should return Error with status and html", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/fail?status=404&html=html-foo-bar`).end(err => { - expect(err).to.be.ok; - expect(err.status).to.equal(404); - expect(err.response.text).contains("html-foo-bar"); - expect(err.response.text).contains("test fail 404"); - server.close(() => resolve()); - }); - }); - }); - - it("should return 404 and html, if custom html is provided", () => { - const server = startServer(webappOptions(null)); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=404&html=NotFoundHTML&render=0`).end( - (err, resp) => { - expect(err).to.be.ok; - expect(resp.status).to.equal(404); - expect(resp.text).to.equal("NotFoundHTML"); - server.close(() => resolve()); - } - ); - }); - }); - - it("should return 410 and html, if custom html is provided", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=410&html=GoneHTML&render=0`).end( - (err, resp) => { - expect(err).to.be.ok; - expect(resp.status).to.equal(410); - expect(resp.text).to.equal("GoneHTML"); - server.close(() => resolve()); - } - ); - }); - }); - - it("should not fail on 404 if no html is provided", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=404&data=test&render=0`).end( - (err, resp) => { - expect(err).to.be.ok; - expect(resp.status).to.equal(404); - server.close(() => resolve()); - } - ); - }); - }); - - it("should return 503 and html, if custom html is provided", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request( - `http://localhost:${port}/status?status=503&html=ServerErrorHTML&render=0` - ).end((err, resp) => { - expect(err).to.be.ok; - expect(resp.status).to.equal(503); - expect(resp.text).to.equal("ServerErrorHTML"); - server.close(() => resolve()); - }); - }); - }); - - it("should return 200 and html with render false", () => { - const server = startServer(webappOptions()); - return new Promise((resolve, reject) => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=200&html=HelloTestHTML&render=0`).end( - (err, resp) => { - if (err) reject(err); - expect(resp.status).to.equal(200); - expect(resp.text).to.equal("HelloTestHTML"); - server.close(() => resolve()); - } - ); - }); - }); - - it("should handle and return 500 on non-render errors", () => { - const options = webappOptions(); - options.tokenHandlers = Path.join(__dirname, "../fixtures/non-render-error"); - const server = startServer(options); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/`).end(err => { - expect(err).to.be.ok; - expect(err.status).to.equal(500); - expect(err.response.text).contains("error from test/fixtures/non-render-error"); - server.close(() => resolve()); - }); - }); - }); - - it("should handle replyErrorStack being false", () => { - const options = webappOptions(); - options.tokenHandlers = Path.join(__dirname, "../fixtures/non-render-error"); - options.replyErrorStack = false; - const server = startServer(options); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/`).end(err => { - expect(err).to.be.ok; - expect(err.status).to.equal(500); - expect(err.response.text).contains("error from test/fixtures/non-render-error"); - // should contain no stack - expect(err.response.text).to.not.contains( - "packages/electrode-react-webapp/test/fixtures/non-render-error.js" - ); - server.close(() => resolve()); - }); - }); - }); - - it("should return 500 on errors", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/fail`).end(err => { - expect(err).to.be.ok; - expect(err.status).to.equal(500); - expect(err.response.text).contains("test fail undefined"); - server.close(() => resolve()); - }); - }); - }); - - it("should return 302 on redirect", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/redirect`) - .redirects(0) - .end((err, resp) => { - expect(resp.text).includes("/redirect2"); - return server.close(() => resolve()); - }); - }); - }); - - it("should return non 200 status", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=401&message=HelloTest401`).end( - (err, resp) => { - expect(resp.status).to.equal(401); - expect(resp.body.message).to.equal("HelloTest401"); - server.close(() => resolve()); - } - ); - }); - }); - - it("should return 200 status with render off", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=200&message=HelloTest200`).end( - (err, resp) => { - expect(resp.status).to.equal(200); - expect(resp.body.message).to.equal("HelloTest200"); - server.close(() => resolve()); - } - ); - }); - }); - - it("should handle content as a string", () => { - const server = startServer(webappOptions()); - return new Promise((resolve, reject) => { - const port = server.address().port; - return request(`http://localhost:${port}/string`).end((err, resp) => { - if (err) reject(err); - expect(resp.text).includes(""); - expect(resp.text).includes(">test content as a string"); - server.close(() => resolve()); - }); - }); - }); - - it("@no-html should return JSON with 200 status with ss off", () => { - const server = startServer(webappOptions()); - return new Promise(resolve => { - const port = server.address().port; - return request(`http://localhost:${port}/status?status=200`).end((err, resp) => { - expect(resp.status).to.equal(200); - expect(resp.body.message).to.equal("no html"); - server.close(() => resolve()); - }); - }); - }); - - it("should setup route to all methods", () => { - const server = startServer(webappOptions()); - return new Promise((resolve, reject) => { - const port = server.address().port; - return request - .post(`http://localhost:${port}/all`) - .send({}) - .end((err, resp) => { - if (err) return reject(err); - expect(resp.text).includes("Test All"); - expect(resp.text).includes("console.log('Hello all');"); - return server.close(() => resolve()); - }); - }); - }); - - it("should handle option serverSideRendering false", () => { - const options = webappOptions(); - options.serverSideRendering = false; - const server = startServer(options); - return new Promise((resolve, reject) => { - const port = server.address().port; - return request.get(`http://localhost:${port}`).end((err, resp) => { - if (err) return reject(err); - expect(resp.text).includes(""); - return server.close(() => resolve()); - }); - }); - }); - - it("should set ssrPrefetchOnly for datass mode", () => { - const options = webappOptions(); - const app = express(); - registerRoutes(app, options); - app.use((req, res, next) => { - req.app = {}; - next(); - }); - const server = app.listen(0); - return new Promise((resolve, reject) => { - const port = server.address().port; - return request.get(`http://localhost:${port}/data_only?__mode=datass`).end((err, resp) => { - if (err) return reject(err); - expect(resp.text).includes("ssrPrefetchOnly true"); - expect(resp.text).includes(`
`); - return server.close(() => resolve()); - }); - }); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/group-scripts.spec.js b/packages/electrode-react-webapp/test/spec/group-scripts.spec.js deleted file mode 100644 index 455dac07b..000000000 --- a/packages/electrode-react-webapp/test/spec/group-scripts.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; - -const data = [ - "a", - "b", - "c", - { src: 1 }, - { src: 5, defer: 1 }, - { src: 2 }, - "d", - "e", - "f;", - "g", - { src: 3 }, - "h", - "i", - "j", - { - src: 4, - async: 1 - }, - { src: 10 }, - "k", - "l", - "m" -]; - -const groupScripts = require("../../lib/group-scripts"); -const chai = require("chai"); - -describe("group-scripts", function() { - it("should group scripts", () => { - const expected = [ - "a;\n\nb;\n\nc;", - [ - { - src: 1 - }, - { - src: 5, - defer: 1 - }, - { - src: 2 - } - ], - "d;\n\ne;\n\nf;\n\ng;", - [ - { - src: 3 - } - ], - "h;\n\ni;\n\nj;", - [ - { - src: 4, - async: 1 - }, - { - src: 10 - } - ], - "k;\n\nl;\n\nm;" - ]; - const result = groupScripts(data); - chai.expect(result.scripts).to.deep.equal(expected); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/hapi.index.spec.js b/packages/electrode-react-webapp/test/spec/hapi.index.spec.js deleted file mode 100644 index 2e500aefc..000000000 --- a/packages/electrode-react-webapp/test/spec/hapi.index.spec.js +++ /dev/null @@ -1,1729 +0,0 @@ -"use strict"; - -/* eslint-disable quotes */ - -const Fs = require("fs"); -const assign = require("object-assign"); -const electrodeServer = require("electrode-server"); -const Path = require("path"); -const xstdout = require("xstdout"); -const { expect } = require("chai"); -const webapp = require("../../lib/hapi/plugin16"); -const ReactDOMServer = require("react-dom/server"); -const React = require("react"); -const Helmet = require("react-helmet").Helmet; -const { runFinally, asyncVerify } = require("run-verify"); -const xaa = require("xaa"); - -describe("hapi 16 electrode-react-webapp", () => { - let config; - let configOptions; - let mainRoutePathOptions; - let testServer; - - const getConfig = () => { - return { - server: { - useDomains: false - }, - connections: { - default: { - port: 0 - } - }, - plugins: { - "react-webapp": { - module: Path.join(__dirname, "../../lib/hapi/plugin16"), - options: { - pageTitle: "Electrode App", - paths: { - "/test/component-redirect": { - content: { - module: Path.join(__dirname, "../router-engine/content.jsx") - } - }, - "/test/verbatim": { - content: { - module: Path.join(__dirname, "../router-engine/content-verbatim.jsx") - } - }, - "/{args*}": { - content: { - status: 200, - html: "
Hello Electrode
", - prefetch: "console.log('Hello');" - } - }, - "/react-helmet": {} - } - } - } - }, - electrode: { - logLevel: "none" - } - }; - }; - - let stdoutIntercept; - - const stopServer = server => { - return new Promise((resolve, reject) => - server.stop(stopErr => { - return stopErr ? reject(stopErr) : resolve(); - }) - ); - }; - - beforeEach(() => { - config = getConfig(); - configOptions = config.plugins["react-webapp"].options; - mainRoutePathOptions = configOptions.paths["/{args*}"]; - }); - - afterEach(() => { - if (stdoutIntercept) { - stdoutIntercept.restore(); - stdoutIntercept = undefined; - } - - if (testServer) { - return stopServer(testServer).then(() => { - testServer = undefined; - }); - } - - return Promise.resolve(); - }); - - it("should fail if registering plugin throws", () => { - let error; - webapp.register( - {}, - { - paths: { - error: { - content: { module: "bad-module" } - } - } - }, - err => { - error = err; - } - ); - expect(error).to.be.ok; - }); - - it("should successfully render with enterHead scripts", () => { - configOptions.unbundledJS = { - enterHead: ["test-static-markup script", { src: "blah-123" }] - }; - - const verify = () => { - return testServer - .inject({ - method: "GET", - url: "/" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain(``); - expect(res.result).to.contain(``); - expect(res.result).to.contain("Electrode App"); - expect(res.result).to.contain("
Hello Electrode
"); - expect(res.result).to.contain(""); - expect(res.result).to.not.contain("Unknown marker"); - }); - }; - return electrodeServer(config).then(server => { - testServer = server; - return verify().then(verify); - }); - }); - - it("should successfully render to static markup", () => { - return electrodeServer(config).then(server => { - const makeRequest = () => { - return server - .inject({ - method: "GET", - url: "/" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("Electrode App"); - expect(res.result).to.contain("
Hello Electrode
"); - expect(res.result).to.contain(""); - expect(res.result).to.not.contain("Unknown marker"); - }); - }; - - return makeRequest() - .then(() => makeRequest()) - .then(() => stopServer(server)) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should successfully render to static markup with insert IDs", () => { - configOptions.insertTokenIds = true; - configOptions.templateProcessor = asyncTemplate => { - asyncTemplate.addTokens({ - insert: "before", - id: "PAGE_TITLE", - tokens: [ - { token: "#./test/fixtures/custom-null" }, - { token: "#./test/fixtures/custom-1" }, - { token: "#./test/fixtures/custom-call", props: { _call: "setup", _noInsertId: true } } - ] - }); - }; - return electrodeServer(config).then(server => { - const makeRequest = () => { - return server - .inject({ - method: "GET", - url: "/" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - const result = res.result; - expect(result).to.contain("Electrode App"); - expect(result).to.contain("
Hello Electrode
"); - expect(result).to.contain(""); - expect(result).to.not.contain("Unknown marker"); - expect(result).contains(""); - expect(result).contains(""); - expect(result).contains(""); - expect(result).contains(""); - expect(result).contains(""); - expect(result).contains(""); - expect(result).contains("_call process from custom-call token fixture"); - expect(result).not.contains(""); - expect(result).contains( - "" - ); - expect(result).contains( - "" - ); - }); - }; - - return makeRequest() - .then(() => makeRequest()) - .then(() => stopServer(server)) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should successfully render to static markup with content as a function", () => { - assign(mainRoutePathOptions, { - content: () => - Promise.resolve({ - status: 200, - html: "
Hello Electrode
", - prefetch: "console.log('Hello');" - }) - }); - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("Electrode App"); - expect(res.result).to.contain("
Hello Electrode
"); - expect(res.result).to.contain(""); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should successfully return 302 redirect", () => { - return electrodeServer(config).then(server => { - return server - .inject({ method: "GET", url: "/test/component-redirect" }) - .then(res => { - expect(res.statusCode).to.equal(302); - expect(res.headers.location).to.equal("/redirect-target"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should successfully use content status verbatim", () => { - let server; - return asyncVerify( - () => electrodeServer(config), - s => { - server = s; - return server.inject({ method: "GET", url: "/test/verbatim" }); - }, - res => { - expect(res.statusCode).to.equal(560); - }, - runFinally(() => stopServer(server)) - ); - }); - - it("should return 500 if content rejects with error", () => { - assign(mainRoutePathOptions, { - content: () => - Promise.reject({ - status: 500, - message: "Internal server error" - }) - }); - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/" - }) - .then(res => { - expect(res.statusCode).to.equal(500); - expect(res.statusMessage).to.equal("Internal Server Error"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should handle multiple entry points - foo", () => { - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/foo" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("foo.bundle.f07a873ce87fc904a6a5.js"); - expect(res.result).to.contain("foo.style.f07a873ce87fc904a6a5.css"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should handle multiple entry points - bar", () => { - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/bar" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("bar.bundle.f07a873ce87fc904a6a5.js"); - expect(res.result).to.contain("bar.style.f07a873ce87fc904a6a5.css"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should handle multiple entry points - multi-chunk", () => { - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/multi-chunk" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("foo.style.f07a873ce87fc904a6a5.css"); - expect(res.result).to.contain("bar.style.f07a873ce87fc904a6a5.css"); - expect(res.result).to.contain("home.bundle.f07a873ce87fc904a6a5.js"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should handle multiple entry points - @dev multi-chunk", () => { - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - process.env.WEBPACK_DEV = "true"; - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/multi-chunk" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("http://localhost:2992/js/foo.style.css"); - expect(res.result).to.contain("http://localhost:2992/js/bar.style.css"); - expect(res.result).to.contain("http://localhost:2992/js/home.bundle.dev.js"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }); - }); - - it("should handle multiple entry points - @dev @empty", () => { - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - process.env.WEBPACK_DEV = "true"; - return electrodeServer(config) - .then(server => { - return server - .inject({ - method: "GET", - url: "/empty" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain("http://localhost:2992/js/bundle.dev.js"); - expect(res.result).to.contain("http://localhost:2992/js/style.css"); - stopServer(server); - }) - .catch(err => { - stopServer(server); - throw err; - }); - }) - .finally(() => { - delete process.env.WEBPACK_DEV; - }); - }); - - it("should handle multiple entry points with a prodBundleBase", () => { - configOptions.prodBundleBase = "http://awesome-cdn.com/multi/"; - configOptions.bundleChunkSelector = "test/data/chunk-selector.js"; - configOptions.stats = "test/data/stats-test-multibundle.json"; - - return electrodeServer(config).then(server => { - return server - .inject({ - method: "GET", - url: "/bar" - }) - .then(res => { - expect(res.statusCode).to.equal(200); - expect(res.result).to.contain( - `
user-promise-token
test html-1
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-promise-token
test html-1
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-promise-token
test html-1
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(res.result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(result).includes( - `
from custom-1
user-token-1
user-token-2
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(result).includes( - `
from custom-1
user-token-1
user-token-2
user-promise-token
test html-1
user-spot-1;user-spot-2;user-spot-3
` // eslint-disable-line - ); - expect(result).includes( - `
from custom-1
user-token-1
user-token-2
"); - }); - }); - - it("@no-html should return JSON with 200 status with ss off", () => { - const server = startServer(webappOptions()); - const port = server.address().port; - return promiseRequest(request(`http://localhost:${port}/status?status=200`)).then(resp => { - expect(resp.status).to.equal(200); - expect(resp.body.message).to.equal("no html"); - }); - }); - - it("should setup route to all methods", () => { - const server = startServer(webappOptions()); - const port = server.address().port; - return promiseRequest(request.post(`http://localhost:${port}/all`).send({})).then(resp => { - expect(resp.text).includes("Test All"); - expect(resp.text).includes("console.log('Hello all');"); - }); - }); - - it("should handle option serverSideRendering false", () => { - const options = webappOptions(); - options.serverSideRendering = false; - const server = startServer(options); - const port = server.address().port; - return promiseRequest(request(`http://localhost:${port}`)).then(resp => { - expect(resp.text).includes(""); - }); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/load-handler.spec.js b/packages/electrode-react-webapp/test/spec/load-handler.spec.js deleted file mode 100644 index 4fded60a1..000000000 --- a/packages/electrode-react-webapp/test/spec/load-handler.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; - -const loadHandler = require("../../lib/load-handler"); -const expect = require("chai").expect; - -describe("token module load handler", function() { - it("should handle ES6 module default", () => { - const x = loadHandler("../fixtures/token-mod-es6-default", __dirname); - expect(x.toString()).contains("function es6Default"); - }); - - it("should handle module exporting tokenHandler", () => { - const x = loadHandler("../fixtures/token-mod-handler", __dirname); - expect(x.toString()).contains("function asTokenHandler"); - }); - - it("should handle module with invalid export", () => { - expect(() => loadHandler("../fixtures/token-mod-invalid", __dirname)).throw( - "token module invalid" - ); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/react-webapp.spec.js b/packages/electrode-react-webapp/test/spec/react-webapp.spec.js deleted file mode 100644 index cf14b27c7..000000000 --- a/packages/electrode-react-webapp/test/spec/react-webapp.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -"use strict"; - -const Path = require("path"); -const reactWebapp = require("../../lib/react-webapp"); -const expect = require("chai").expect; -const xstdout = require("xstdout"); - -describe("react-webapp", function() { - describe("resolveContent", function() { - it("should require module with relative path", () => { - const f = "./test/data/foo.js"; - expect(reactWebapp.resolveContent({ module: f }).content).to.equal("hello"); - }); - - it("should log error if resolving content fail", () => { - const intercept = xstdout.intercept(true); - const f = "./test/data/bad-content.js"; - const content = reactWebapp.resolveContent({ module: f }); - intercept.restore(); - expect(content.content).includes("test/data/bad-content.js"); - expect(intercept.stderr.join("")).includes("Error: Cannot find module 'foo-blah'"); - }); - - it("should require module", () => { - let mod; - const fooRequire = x => (mod = x); - fooRequire.resolve = x => x; - const f = "test"; - const content = reactWebapp.resolveContent({ module: f }, fooRequire); - expect(content.content).to.equal(f); - expect(content.fullPath).to.equal(f); - expect(mod).to.equal(f); - }); - }); - - describe("makeRouteHandler", () => { - it("should not add default handler if it's already included in options", () => { - const htmlFile = Path.resolve("test/fixtures/test-render-context.html"); - const defaultReactHandler = Path.join(__dirname, "../../lib/react/token-handlers"); - const intercept = xstdout.intercept(false); - const handleRoute = reactWebapp.makeRouteHandler({ - htmlFile, - tokenHandlers: [ - { - name: "internal-test-handler", - beforeRender: context => { - expect(context.user).to.equal(false); - context.handleError = () => {}; - }, - afterRender: context => { - expect(context.user, "should have context.user").to.not.equal(false); - }, - tokens: { - "internal-test": () => "\ninternal-test", - "test-not-found": () => "\nnot found", - "non-func-token": "" - } - }, - defaultReactHandler - ], - __internals: { assets: {}, chunkSelector: () => ({}) } - }); - - const promise = handleRoute({ request: {}, content: { status: 200, html: "" } }); - - return promise - .then(context => { - intercept.restore(); - const expected = ` -from wants next module -from async ok module -from async error module -from string only module -internal-test -from async error module -from wants next module -not found -from string only module -from async ok module`; - expect(context.result).to.equal(expected); - }) - .catch(err => { - intercept.restore(); - throw err; - }); - }); - - it("should not add default handler replaceTokenHandlers is true", () => { - const htmlFile = Path.resolve("test/fixtures/test-render-context.html"); - const intercept = xstdout.intercept(false); - const handleRoute = reactWebapp.makeRouteHandler({ - htmlFile, - replaceTokenHandlers: true, - tokenHandlers: [ - { - name: "internal-test-handler", - beforeRender: context => { - expect(context.user).to.equal(false); - context.handleError = () => {}; - }, - tokens: { - "internal-test": () => "\ninternal-test", - "test-not-found": () => "\nnot found", - "non-func-token": "" - } - } - ], - __internals: { assets: {}, chunkSelector: () => ({}) } - }); - - const promise = handleRoute({ request: {}, content: { status: 200, html: "" } }); - - return promise - .then(context => { - intercept.restore(); - const expected = ` -from wants next module -from async ok module -from async error module -from string only module -internal-test -from async error module -from wants next module -not found -from string only module -from async ok module`; - expect(context.result).to.equal(expected); - }) - .catch(err => { - intercept.restore(); - throw err; - }); - }); - }); - - describe("setupOptions", function() { - it("should enable https if ENV is set", () => { - process.env.WEBPACK_DEV_HTTPS = "true"; - const opt = reactWebapp.setupOptions({}); - expect(opt.__internals.devBundleBase).to.match(/^https:/); - }); - - it(`should not enable https if ENV is set to "false"`, () => { - process.env.WEBPACK_DEV_HTTPS = "false"; - const opt = reactWebapp.setupOptions({}); - expect(opt.__internals.devBundleBase).not.to.match(/^https:/); - }); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/react/content.spec.js b/packages/electrode-react-webapp/test/spec/react/content.spec.js deleted file mode 100644 index 951d18393..000000000 --- a/packages/electrode-react-webapp/test/spec/react/content.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -"use stritct"; - -const Path = require("path"); -const content = require("../../../lib/react/content"); - -describe("content", function() { - let saveEnv; - const cwd = process.cwd(); - - before(() => { - saveEnv = process.env.NODE_ENV; - }); - - after(() => { - if (saveEnv !== undefined) { - process.env.NODE_ENV = saveEnv; - } else { - delete process.env.NODE_ENV; - } - }); - - afterEach(() => { - process.chdir(cwd); - }); - - describe("loadElectrodeDllAssets", function() { - it("should return empty if unable to load asset data", () => { - expect(content.loadElectrodeDllAssets()).to.deep.equal({}); - expect(content.loadElectrodeDllAssets({})).to.deep.equal({}); - expect(content.loadElectrodeDllAssets({ electrodeDllAssetsPath: "blahblah" })).to.deep.equal( - {} - ); - }); - - it("should return load and return asset data", () => { - expect( - content.loadElectrodeDllAssets({ electrodeDllAssetsPath: "test/data/test-dll-assets.json" }) - ).to.deep.equal({ foo: { bar: [1] } }); - }); - - it("should load default asset data for dev mode", () => { - process.env.NODE_ENV = ""; - process.chdir(Path.join(__dirname, "../../data")); - expect(content.loadElectrodeDllAssets({})).to.deep.equal({ production: false }); - }); - - it("should load default asset data for prod mode", () => { - process.env.NODE_ENV = "production"; - process.chdir(Path.join(__dirname, "../../data")); - expect(content.loadElectrodeDllAssets({})).to.deep.equal({ production: true }); - }); - }); - - describe("makeElectrodeDllScripts", function() { - it("should return empty string for empty asset", () => { - expect(content.makeElectrodeDllScripts({})).to.equal(""); - }); - - it("should return scripts for DLL asset in cdnMapping", () => { - const script = content.makeElectrodeDllScripts({ - foo: { - cdnMapping: { - a: "test-a.js", - b: "test-b.js" - } - } - }); - - console.log(script); - expect(script).to.equal(` -`); - }); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/react/handlers/prefetch-bundles.spec.js b/packages/electrode-react-webapp/test/spec/react/handlers/prefetch-bundles.spec.js deleted file mode 100644 index a5be8b32b..000000000 --- a/packages/electrode-react-webapp/test/spec/react/handlers/prefetch-bundles.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; - -const handler = require("../../../../lib/react/handlers/prefetch-bundles"); - -describe("prefetch bundles handler", function() { - it("should return empty string if no prefetch in content", () => { - expect(handler({ user: { content: {} } })).to.equal(""); - }); - - it("should return " - ); - }); - - it("should not return " } } - }) - ).to.equal(""); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/render-context.spec.js b/packages/electrode-react-webapp/test/spec/render-context.spec.js deleted file mode 100644 index 886183d1e..000000000 --- a/packages/electrode-react-webapp/test/spec/render-context.spec.js +++ /dev/null @@ -1,87 +0,0 @@ -"use strict"; - -const Path = require("path"); -const AsyncTemplate = require("../../lib/async-template"); -const RenderContext = require("../../lib/render-context"); -const xaa = require("xaa"); -const xstdout = require("xstdout"); - -const expect = require("chai").expect; - -describe("render-context", function() { - it("should handle setting all stop modes", () => { - const context = new RenderContext({}, {}); - context.stop = RenderContext.FULL_STOP; - expect(context.isFullStop).to.equal(true); - context.softStop(); - expect(context.isSoftStop).to.equal(true); - context.voidStop(); - expect(context.isVoidStop).to.equal(true); - context.fullStop(); - expect(context.isFullStop).to.equal(true); - }); - - it("should render template with all token types", () => { - const htmlFile = Path.resolve("test/fixtures/test-render-context.html"); - let internalHandler = {}; - let internalTokens = {}; - let receivedResult = ""; - const send = x => { - receivedResult += x; - }; - const errors = []; - const asyncTemplate = new AsyncTemplate({ - htmlFile, - tokenHandlers: { - name: "internal-test-handler", - beforeRender: context => { - context.setOutputSend(send); - internalTokens = context.getTokens("internal-test-handler"); - context.handleError = err => errors.push(err); - }, - afterRender: context => { - internalHandler = context.getTokenHandler("internal-test-handler"); - }, - tokens: { - INITIALIZE: "", - "internal-test": () => `\nbuilt-in for internal-test`, - "non-func-token": "\ntest non-func token" - } - } - }); - asyncTemplate.initializeRenderer(); - - const expected = ` -from wants next module -from async ok module -from async error module -from string only module -built-in for internal-test -test non-func token -from async error module -from wants next module -from string only module -from async ok module`; - const intercept = xstdout.intercept(false); - return xaa - .wrap(() => asyncTemplate.render({})) - .then(context => { - intercept.restore(); - expect( - internalHandler.name, - "lookup internal-test-handler with context.getTokenHandler failed" - ).to.equal("internal-test-handler"); - expect(internalTokens).to.have.property("internal-test"); - expect(errors).to.deep.equal([ - "error from test/fixtures/async-error", - "error from test/fixtures/async-error" - ]); - expect(context.result).to.equal(""); - expect(receivedResult).to.equal(expected); - }) - .catch(err => { - intercept.restore(); - throw err; - }); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/render-output.spec.js b/packages/electrode-react-webapp/test/spec/render-output.spec.js deleted file mode 100644 index 2fbd0a247..000000000 --- a/packages/electrode-react-webapp/test/spec/render-output.spec.js +++ /dev/null @@ -1,202 +0,0 @@ -"use strict"; - -const RenderOutput = require("../../lib/render-output"); -const Munchy = require("munchy"); -const streamToArray = require("stream-to-array"); - -const expect = require("chai").expect; - -describe("render-output", function() { - it("should flush simple string", () => { - let text; - const context = { - send: x => (text = x) - }; - const ro = new RenderOutput(context); - ro.add("hello world"); - ro.flush(); - expect(text).to.equal("hello world"); - ro.flush(); - expect(text).to.equal("hello world"); - }); - - it("should flush multiple strings", () => { - let text; - const context = { - send: x => (text = x) - }; - - const ro = new RenderOutput(context); - ro.add("hello world"); - ro.add("foo bar"); - ro.flush(); - expect(text).to.equal("hello worldfoo bar"); - }); - - const testFlushPending = (ro, context) => { - ro.add("hello world"); - ro.add("foo bar"); - const spot = ro.reserve(); - ro.add("after pending"); - ro.add("baz"); - ro.flush(); - expect(context.text).to.equal(undefined); - spot.add("123"); - spot.add("456"); - spot.close(); - expect(context.text).to.equal("hello worldfoo bar123456after pendingbaz"); - }; - - it("should wait on flush when there's pending", () => { - const context = { - text: undefined - }; - context.send = x => (context.text = x); - const ro = new RenderOutput(context); - testFlushPending(ro, context); - }); - - it("should continue output after flush", () => { - const context = { - text: undefined - }; - context.send = x => (context.text = x); - const ro = new RenderOutput(context); - testFlushPending(ro, context); - context.text = undefined; - testFlushPending(ro, context); - }); - - it("should handle multiple spots", () => { - let text = ""; - const context = { - send: x => (text += x) - }; - const ro = new RenderOutput(context); - ro.add("hello world"); - ro.add("foo bar"); - const spot1 = ro.reserve(); - ro.add("after spot1"); - spot1.add("spot1 123"); - ro.add("baz1"); - ro.flush(); - expect(text).to.equal(""); - const spot2 = ro.reserve(); - spot2.add("spot2 123"); - spot2.add("456"); - spot2.close(); - expect(text).to.equal(""); - spot1.add("spot1 abc"); - spot1.add("789"); - ro.add("closing"); - spot1.close(); - ro.flush(); - expect(text).to.equal( - "hello worldfoo barspot1 123spot1 abc789after spot1baz1spot2 123456closing" - ); - }); - - it("should handle multiple buffer and stream data", done => { - const context = { - munchy: new Munchy(), - transform: a => a - }; - - streamToArray(context.munchy, (err, arr) => { - if (err) return done(err); - try { - expect(arr.map(x => x.toString())).to.deep.equal([ - "hello world", - "foo bar", - "spot1 123", - "spot1 abc", - "spot1 a stream", - "789", - "after spot1", - "baz1", - "spot2 123", - "spot2 456", - "spot2 a buffer", - "closing" - ]); - return done(); - } catch (err2) { - return done(err2); - } - }); - - const ro = new RenderOutput(context); - ro.add("hello world"); - ro.add("foo bar"); - const spot1 = ro.reserve(); - ro.add("after spot1"); - spot1.add("spot1 123"); - ro.add("baz1"); - ro.flush(); - const spot2 = ro.reserve(); - spot2.add("spot2 123"); - spot2.add("spot2 456"); - spot2.add(Buffer.from("spot2 a buffer")); - spot2.close(); - spot1.add("spot1 abc"); - spot1.add(new Munchy({}, "spot1 a stream", null)); - spot1.add("789"); - ro.add("closing"); - spot1.close(); - ro.close(); - }); - - it("should delegate result to a promise w/o context and send", () => { - const ro = new RenderOutput(); - ro.add("hello world"); - ro.add("foo bar"); - const spot1 = ro.reserve(); - ro.add("after spot1"); - spot1.add("spot1 123"); - ro.add("baz1"); - ro.flush(); - expect(ro._result).to.equal(""); - const spot2 = ro.reserve(); - spot2.add("spot2 123"); - spot2.add("456"); - spot2.close(); - expect(ro._result).to.equal(""); - spot1.add("spot1 abc"); - spot1.add("789"); - ro.add("closing"); - spot1.close(); - return ro.close().then(result => { - expect(result).to.equal( - "hello worldfoo barspot1 123spot1 abc789after spot1baz1spot2 123456closing" - ); - }); - }); - - it("should not munch if no items", done => { - const ro = new RenderOutput(); - ro._output.sendToMunchy(null, done); - }); - - it("should rethrow if no _reject is available in _finish", () => { - const ro = new RenderOutput(); - ro._reject = undefined; - ro._resolve = () => { - throw new Error("test error"); - }; - expect(() => ro._finish()).to.throw("test error"); - }); - - it("should throw if can't stringify an item", () => { - const ro = new RenderOutput(); - ro.add({}); - expect(() => ro.flush()).to.throw("unable to stringify item of type Object"); - }); - - it("should throw if can't stringify an item without constructor", () => { - const ro = new RenderOutput(); - const item = {}; - item.constructor = false; - ro.add(item); - expect(() => ro.flush()).to.throw("unable to stringify item of type object"); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/token.spec.js b/packages/electrode-react-webapp/test/spec/token.spec.js deleted file mode 100644 index 06adb4ac6..000000000 --- a/packages/electrode-react-webapp/test/spec/token.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -const Token = require("../../lib/token"); -const expect = require("chai").expect; -const xstdout = require("xstdout"); -const { TOKEN_HANDLER } = require("../../lib/symbols"); - -describe("token", function() { - it("should create token as internal", () => { - const tk = new Token("test", 50); - expect(tk.id).to.equal("test"); - expect(tk.isModule).to.equal(false); - expect(tk.pos).to.equal(50); - tk.load(); - expect(tk.custom).to.equal(undefined); - }); - - it("should invoke _call of a module", () => { - const tk = new Token("#./test/fixtures/custom-call", 0, { _call: "setup" }); - expect(tk.id).to.equal("#./test/fixtures/custom-call"); - expect(tk.isModule).to.equal(true); - tk.load(); - const promise = tk[TOKEN_HANDLER](); - expect(promise).to.exist; - return promise.then(r => { - expect(r).to.equal("_call process from custom-call token fixture"); - }); - }); - - it("should create token as custom and call setup only once for each token", () => { - const tk1 = new Token("#./test/fixtures/custom-count"); - expect(tk1.id).to.equal("#./test/fixtures/custom-count"); - expect(tk1.isModule).to.equal(true); - tk1.load(); - expect(tk1[TOKEN_HANDLER]()).to.equal("1"); - tk1.load(); // test re-entry - expect(tk1[TOKEN_HANDLER]()).to.equal("1"); - const tk2 = new Token("#./test/fixtures/custom-count"); - expect(tk2.id).to.equal("#./test/fixtures/custom-count"); - expect(tk2.isModule).to.equal(true); - tk2.load(); - expect(tk2[TOKEN_HANDLER]()).to.equal("2"); - }); - - it("should handle custom module not found", () => { - const tk = new Token("#./test/fixtures/not-found"); - expect(tk.id).to.equal("#./test/fixtures/not-found"); - expect(tk.isModule).to.equal(true); - const intercept = xstdout.intercept(true); - tk.load(); - intercept.restore(); - expect(tk[TOKEN_HANDLER]()).to.equal( - "\ntoken process module ./test/fixtures/not-found not found\n" - ); - }); - - it("should handle custom module load failure", () => { - const tk = new Token("#./test/fixtures/custom-fail"); - expect(tk.id).to.equal("#./test/fixtures/custom-fail"); - expect(tk.isModule).to.equal(true); - const intercept = xstdout.intercept(true); - tk.load(); - intercept.restore(); - expect(tk[TOKEN_HANDLER]()).to.equal( - "\ntoken process module ./test/fixtures/custom-fail failed to load\n" - ); - }); - - it("should handle custom module returning null", () => { - const tk = new Token("#./test/fixtures/custom-null"); - expect(tk.id).to.equal("#./test/fixtures/custom-null"); - expect(tk.isModule).to.equal(true); - expect(tk.custom).to.equal(undefined); - tk.load(); - expect(tk.custom).to.equal(null); - }); -}); diff --git a/packages/electrode-react-webapp/test/spec/utils.spec.js b/packages/electrode-react-webapp/test/spec/utils.spec.js deleted file mode 100644 index a98d7e3b2..000000000 --- a/packages/electrode-react-webapp/test/spec/utils.spec.js +++ /dev/null @@ -1,359 +0,0 @@ -"use strict"; - -const expect = require("chai").expect; -const Fs = require("fs"); -const utils = require("../../lib/utils"); -const Path = require("path"); -const sinon = require("sinon"); - -describe("utils", function() { - it("getIconStats should return stats w/o html as is", () => { - const x = utils.getIconStats("test/data/icon-stats-empty.json"); - expect(x).to.deep.equal({ empty: true }); - }); - - it("getStatsPath should return diff path with webpack dev", () => { - const x = utils.getStatsPath("dist/stats.json", "build"); - expect(x).to.equal("dist/stats.json"); - process.env.WEBPACK_DEV = "true"; - const x2 = utils.getStatsPath("dist/stats.json", "build"); - expect(x2).to.equal(Path.resolve("build", "stats.json")); - delete process.env.WEBPACK_DEV; - }); - - describe("htmlifyError", function() { - let save; - beforeEach(() => { - if (process.env.hasOwnProperty("NODE_ENV")) { - save = process.env.NODE_ENV; - } - }); - - afterEach(() => { - delete process.env.NODE_ENV; - if (save !== undefined) { - process.env.NODE_ENV = save; - save = undefined; - } - }); - - it("should return doc with err.html and err.stack", () => { - const err = new Error("test1"); - err.html = "foo bar"; - const r = utils.htmlifyError(err); - expect(r).contains("foo bar"); - expect(r).contains("/test/spec/utils.spec.js"); - }); - - it("should return err.message if err.stack is not avail somehow", () => { - const err = { message: "hello world" }; - const r = utils.htmlifyError(err); - expect(r).contains("hello world"); - }); - - it("should return doc w/o err.stack in production", () => { - const err = new Error("test1"); - err.html = "foo bar"; - process.env.NODE_ENV = "production"; - const r = utils.htmlifyError(err); - expect(r).contains("foo bar"); - expect(r).to.not.contains("/test/spec/utils.spec.js"); - }); - - it("should return doc w/o err.stack if withStack is false", () => { - const err = new Error("test1"); - err.html = "foo bar"; - process.env.NODE_ENV = "development"; - const r = utils.htmlifyError(err, false); - expect(r).contains("foo bar"); - expect(r).to.not.contains("/test/spec/utils.spec.js"); - }); - }); - - describe("getCspNonce", () => { - it("should get no CSP nonce if option is not defined", () => { - const nonce = utils.getCspNonce( - { - app: { - scriptCsp: "csp-script", - styleCsp: "csp-style" - } - }, - null - ); - expect(nonce).to.deep.equal({ - scriptNonce: ``, - styleNonce: `` - }); - }); - - it("should call function with request to get CSP nonce", () => { - const nonce = utils.getCspNonce({}, (request, tag) => { - if (tag === "script") return "csp-script"; - if (tag === "style") return "csp-style"; - return ""; - }); - expect(nonce).to.deep.equal({ - scriptNonce: ` nonce="csp-script"`, - styleNonce: ` nonce="csp-style"` - }); - }); - - it("should get CSP nonce from request base on option path", () => { - const nonce = utils.getCspNonce( - { - app: { - scriptCsp: "csp-script", - styleCsp: "csp-style" - } - }, - { - script: "app.scriptCsp", - style: "app.styleCsp" - } - ); - expect(nonce).to.deep.equal({ - scriptNonce: ` nonce="csp-script"`, - styleNonce: ` nonce="csp-style"` - }); - }); - - it("should get no CSP nonce if no value can be retrieved", () => { - const nonce = utils.getCspNonce( - { - app: {} - }, - { - script: "app.scriptCsp", - style: "app.styleCsp" - } - ); - expect(nonce).to.deep.equal({ - scriptNonce: ``, - styleNonce: `` - }); - }); - }); - - describe("getCriticalCSS", () => { - it("should return empty string if loading failed", () => { - const x = utils.getCriticalCSS("blah"); - expect(x).to.equal(""); - }); - - it("should load CSS from a file", () => { - const x = utils.getCriticalCSS("test/data/critical.css"); - expect(x).to.equal(`body {color: green;} -`); - }); - }); - - describe("processRenderSsMode", () => { - it("should return false for renderSs option being false", () => { - expect(utils.processRenderSsMode({}, false, "")).to.equal(false); - }); - - it("should return false for renderSs mode being noss", () => { - expect(utils.processRenderSsMode({}, true, "noss")).to.equal(false); - }); - - it("should return datass for renderSs and set ssrPrefetchOnly flat in request", () => { - const request = {}; - expect(utils.processRenderSsMode(request, "datass", "")).to.equal("datass"); - expect(request.app.ssrPrefetchOnly).to.equal(true); - }); - - it("should return datass for mode and set ssrPrefetchOnly flat in request", () => { - const request = {}; - expect(utils.processRenderSsMode(request, true, "datass")).to.equal("datass"); - expect(request.app.ssrPrefetchOnly).to.equal(true); - }); - }); - - describe("invokeTemplateProcessor", () => { - it("should execute templateProcessor function directly", () => { - let resA; - let resB; - const templateProcessor = (a, b) => { - resA = a; - resB = b; - }; - - const asyncTemplate = {}; - const options = { templateProcessor }; - - utils.invokeTemplateProcessor(asyncTemplate, options); - expect(resA).to.equal(asyncTemplate); - expect(resB).to.equal(options); - }); - - it("should throw if templateProcessor is not a function", () => { - const asyncTemplate = {}; - const options = { templateProcessor: true }; - - expect(() => utils.invokeTemplateProcessor(asyncTemplate, options)).to.throw( - "is not a function" - ); - }); - - it("should load function exported from module", () => { - const asyncTemplate = {}; - const options = { templateProcessor: "./test/fixtures/template-processor-1" }; - - expect(utils.invokeTemplateProcessor(asyncTemplate, options)).to.equal( - "template-processor-1" - ); - }); - - it("should load defined function exported from module", () => { - const asyncTemplate = {}; - const options = { templateProcessor: "./test/fixtures/template-processor-2" }; - - expect(utils.invokeTemplateProcessor(asyncTemplate, options)).to.equal( - "template-processor-2" - ); - }); - - it("should throw if module doesn't export usable function", () => { - const asyncTemplate = {}; - const options = { templateProcessor: "./test/fixtures/template-processor-3" }; - - expect(() => utils.invokeTemplateProcessor(asyncTemplate, options)).to.throw( - "doesn't export a usable function" - ); - }); - }); - - describe("getOtherStats", () => { - it("should require stats file if dist/server exists", () => { - const fakeExistsSync = sinon.stub(Fs, "existsSync").callsFake(() => true); - const fakeReaddirSync = sinon - .stub(Fs, "readdirSync") - .callsFake(() => [Path.resolve("es5-stats.json"), Path.resolve("es6-stats.json")]); - const otherStats = utils.getOtherStats(); - fakeExistsSync.restore(); - fakeReaddirSync.restore(); - const keys = Object.keys(otherStats); - expect(keys.includes("es5")).be.true; - expect(keys.includes("es6")).be.true; - }); - }); - - describe("getOtherAssets", () => { - it("should generate otherAssets if otherStats is not empty", () => { - const otherStats = { - es5: Path.resolve("es5-stats.json"), - es6: Path.resolve("es6-stats.json") - }; - const buildArtifacts = ".build"; - const pluginOptions = { otherStats, buildArtifacts }; - const otherAssets = utils.getOtherAssets(pluginOptions, utils.loadAssetsFromStats); - const keys = Object.keys(otherAssets); - expect(keys.includes("es5")).be.true; - expect(keys.includes("es6")).be.true; - }); - }); - - describe("getBundleJsNameByQuery", () => { - it("should get file name ends with main.bundle.js", () => { - const data = { - jsChunk: { name: "default.main.bundle.js" } - }; - const otherAssets = { - es6: { js: [{ name: "es6.main.bundle.js" }] } - }; - const es6 = utils.getBundleJsNameByQuery( - Object.assign(data, { - query: { __dist: "es6" } - }), - otherAssets - ); - expect(es6).to.equal(otherAssets.es6.js[0].name); - const es5 = utils.getBundleJsNameByQuery( - Object.assign(data, { - query: { __dist: "es5" } - }), - otherAssets - ); - expect(es5).to.equal(data.jsChunk.name); - }); - }); - - describe("munchyHandleStreamError", () => { - it("should return error with stacks", () => { - const output = utils.munchyHandleStreamError(new Error("blah blah")); - expect(output.result).contains("Error: blah blah"); - expect(output.result).contains("test/spec/utils.spec.js:"); - expect(output.result).contains("CWD/"); - expect(output.remit).to.equal(false); - }); - - it("should not replace cwd shorter than 4 chars", () => { - const save = process.cwd(); - process.chdir("/"); - const output = utils.munchyHandleStreamError(new Error("blah blah")); - expect(output.result).contains("Error: blah blah"); - expect(output.result).contains("test/spec/utils.spec.js:"); - expect(output.result).not.contains("CWD/"); - expect(output.remit).to.equal(false); - process.chdir(save); - }); - - it("should return error without stacks in production", () => { - const save = process.env.NODE_ENV; - process.env.NODE_ENV = "production"; - const output = utils.munchyHandleStreamError(new Error()); - expect(output.result).not.contains("test/spec/utils.spec.js:"); - expect(output.remit).to.equal(false); - if (save) { - process.env.NODE_ENV = save; - } - }); - }); - - describe("makeDevBundleBase", () => { - beforeEach(() => { - delete process.env.WEBPACK_DEV_CDN_PROTOCOL; - delete process.env.WEBPACK_DEV_CDN_HOSTNAME; - delete process.env.WEBPACK_DEV_CDN_PORT; - delete process.env.APP_SERVER_PORT; - }); - - it("should use WEBPACK_DEV_CDN_HOSTNAME env variables", () => { - process.env.WEBPACK_DEV_CDN_HOSTNAME = "testhost"; - const baseUrl = utils.makeDevBundleBase(); - expect(baseUrl).to.equal("http://testhost/js/"); - }); - - it("should use WEBPACK_DEV_CDN_PROTOCOL env variables", () => { - process.env.WEBPACK_DEV_CDN_PROTOCOL = "https"; - const baseUrl = utils.makeDevBundleBase(); - expect(baseUrl).to.equal("https://localhost/js/"); - }); - - it("should use WEBPACK_DEV_CDN_PORT env variables", () => { - process.env.WEBPACK_DEV_CDN_PORT = "8080"; - const baseUrl = utils.makeDevBundleBase(); - expect(baseUrl).to.equal("http://localhost:8080/js/"); - }); - - it("should ignore WEBPACK_DEV_CDN_PORT env variables if it's 80", () => { - process.env.WEBPACK_DEV_CDN_PORT = "80"; - const baseUrl = utils.makeDevBundleBase(); - expect(baseUrl).to.equal("http://localhost/js/"); - }); - - it("should check process.env.APP_SERVER_PORT", () => { - process.env.APP_SERVER_PORT = "8080"; - const baseUrl = utils.makeDevBundleBase(); - expect(baseUrl).to.equal("/js/"); - }); - }); - - describe("getProdBundles", function() { - it("should return empty object if no route data or assets", () => { - expect(utils.getProdBundles()).to.deep.equal({}); - expect(utils.getProdBundles("", {})).to.deep.equal({}); - }); - }); -}); diff --git a/packages/electrode-react-webapp/xclap.js b/packages/electrode-react-webapp/xclap.js deleted file mode 100644 index ea371779c..000000000 --- a/packages/electrode-react-webapp/xclap.js +++ /dev/null @@ -1 +0,0 @@ -require("electrode-archetype-njs-module-dev")();