Skip to content

Commit

Permalink
Move fastboot specific config to a meta tag; keep manifest in
Browse files Browse the repository at this point in the history
package.json for broccoli-asset-rev support

This commit builds fastboot specific config (available via
FastBoot.config()) to meta tag, which will be extracted by fastboot
when served.

The fastboot config meta has the name ending with
"config/fastboot-environement" to distinguish from regular
"config/environement" meta tags. If the new meta tags are not found,
which happens in embroider builds, we fallback to find regular meta
tags.
  • Loading branch information
xg-wang committed Apr 9, 2021
1 parent c4d2dbc commit 9068d29
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 84,251 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
- name: Yarn Install
run: yarn install --ignore-engines --frozen-lockfile
- name: Integration Tests
run: yarn workspace integration-tests test
run: yarn workspace integration-tests test

test-packages:
name: Test Packages
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"test:fastboot": "yarn workspace fastboot test",
"test:fastboot-express-middleware": "yarn workspace fastboot-express-middleware test",
"test:fastboot-app-server": "yarn workspace fastboot-app-server test:mocha",
"test:integration": "yarn workspace integration-tests test"
"test:integration": "yarn workspace integration-tests test",
"test:extra": "yarn workspace basic-app test:mocha && yarn workspace custom-fastboot-app test:mocha"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
Expand Down
4 changes: 3 additions & 1 deletion packages/ember-cli-fastboot/lib/broccoli/fastboot-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ module.exports = class FastBootConfig extends Plugin {
moduleWhitelist: this.moduleWhitelist,
schemaVersion: LATEST_SCHEMA_VERSION,
hostWhitelist: this.normalizeHostWhitelist(),
config: this.fastbootConfig,
// We can't drop manifest until broccoli-asset-rev also supports v5 HTML based manifest
// https://github.com/ember-cli/broccoli-asset-rev/blob/78f6047c15acb3bd348611f658b03bdd1041911f/lib/fastboot-manifest-rewrite.js
manifest: this.manifest,
htmlEntrypoint: this.manifest.htmlFile
}
},
Expand Down
52 changes: 50 additions & 2 deletions packages/ember-cli-fastboot/lib/broccoli/html-writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = class BasePageWriter extends Filter {
});
this._manifest = manifest;
this._rootURL = getRootURL(fastbootConfig, appName);
this._fastbootConfig = fastbootConfig;
this._appJsPath = outputPaths.app.js;
this._expectedFiles = expectedFiles(outputPaths);
}
Expand All @@ -24,15 +25,46 @@ module.exports = class BasePageWriter extends Filter {

processString(content) {
let dom = new JSDOM(content);
this._handleConfig(dom);
this._handleScripts(dom);
return dom.serialize();
}

_handleConfig(dom) {
function findFistConfigMeta(dom) {
let metaTags = dom.window.document.querySelectorAll('meta');
for (let element of metaTags) {
let name = element.getAttribute('name');
if (name && name.endsWith('/config/environment')) {
return element;
}
}
}
let firstConfigMeta;
if (firstConfigMeta) {
firstConfigMeta = findFistConfigMeta(dom);
} else {
firstConfigMeta = dom.window.document.createTextNode('\n');
dom.window.document.head.appendChild(firstConfigMeta);
}
let nodeRange = new NodeRange(firstConfigMeta);
for (let [name, options] of Object.entries(this._fastbootConfig)) {
nodeRange.insertJsonAsMetaTag(`${name}/config/fastboot-environment`, options);
}
}

_handleScripts(dom) {
let scriptTags = dom.window.document.querySelectorAll('script');

this._ignoreUnexpectedScripts(scriptTags);

let fastbootScripts = this._findFastbootScriptToInsert(scriptTags);
let appJsTag = findAppJsTag(scriptTags, this._appJsPath, this._rootURL);
insertFastbootScriptsBeforeAppJsTags(fastbootScripts, appJsTag);
if (!appJsTag) {
throw new Error('ember-cli-fastboot cannot find own app script tag');
}

return dom.serialize();
insertFastbootScriptsBeforeAppJsTags(fastbootScripts, appJsTag);
}

_findFastbootScriptToInsert(scriptTags) {
Expand Down Expand Up @@ -79,6 +111,10 @@ function getRootURL(appName, config) {
}

function urlWithin(candidate, root) {
// this is a null or relative path
if (!candidate || !candidate.startsWith('/')) {
return candidate;
}
let candidateURL = new URL(candidate, 'http://_the_current_origin_');
let rootURL = new URL(root, 'http://_the_current_origin_');
if (candidateURL.href.startsWith(rootURL.href)) {
Expand Down Expand Up @@ -114,6 +150,18 @@ class NodeRange {
let newTag = this.end.ownerDocument.createElement('fastboot-script');
newTag.setAttribute('src', src);
this.insertNode(newTag);
this.insertNewLine();
}

insertJsonAsMetaTag(name, content) {
let newTag = this.end.ownerDocument.createElement('meta');
newTag.setAttribute('name', name);
newTag.setAttribute('content', encodeURIComponent(JSON.stringify(content)));
this.insertNode(newTag);
this.insertNewLine();
}

insertNewLine() {
this.insertNode(this.end.ownerDocument.createTextNode('\n'));
}

Expand Down
3 changes: 1 addition & 2 deletions packages/fastboot/src/fastboot-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ function loadConfig(distPath) {
({ appName, config, html, scripts } = loadManifest(distPath, pkg.fastboot, schemaVersion));
} else {
appName = pkg.name;
config = pkg.fastboot.config;
({ html, scripts } = htmlEntrypoint(appName, distPath, pkg.fastboot.htmlEntrypoint, config));
({ config, html, scripts } = htmlEntrypoint(appName, distPath, pkg.fastboot.htmlEntrypoint));
}

let sandboxRequire = buildWhitelistedRequire(
Expand Down
30 changes: 27 additions & 3 deletions packages/fastboot/src/html-entrypoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,35 @@ const { JSDOM } = require('jsdom');
const fs = require('fs');
const path = require('path');

function htmlEntrypoint(appName, distPath, htmlPath, config = {}) {
function htmlEntrypoint(appName, distPath, htmlPath) {
let html = fs.readFileSync(path.join(distPath, htmlPath), 'utf8');
let dom = new JSDOM(html);
let scripts = [];

function mergeContent(metaElement, config, configName) {
let name = metaElement.getAttribute('name');
if (name && name.endsWith(configName)) {
let content = JSON.parse(decodeURIComponent(metaElement.getAttribute('content')));
content.APP = Object.assign({ autoboot: false }, content.APP);
config[name.slice(0, -1 * configName.length)] = content;
return true;
}
return false;
}
let fastbootConfig = {};
let config = {};
for (let element of dom.window.document.querySelectorAll('meta')) {
mergeContent(element, config, '/config/environment');
let fastbootMerged = mergeContent(element, fastbootConfig, '/config/fastboot-environment');
if (fastbootMerged) {
element.remove();
}
}
let isFastbootConfigBuilt = Object.keys(fastbootConfig).length > 0;
if (isFastbootConfigBuilt) {
config = fastbootConfig;
}

let scripts = [];
let rootURL = getRootURL(appName, config);

for (let element of dom.window.document.querySelectorAll('script,fastboot-script')) {
Expand All @@ -24,7 +48,7 @@ function htmlEntrypoint(appName, distPath, htmlPath, config = {}) {
}
}

return { html: dom.serialize(), scripts };
return { config, html: dom.serialize(), scripts };
}

function extractSrc(element) {
Expand Down
14 changes: 0 additions & 14 deletions packages/fastboot/test/fastboot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,18 +455,4 @@ describe('FastBoot', function() {
usedPrebuiltSandbox: true,
});
});

it('htmlEntrypoint works', function() {
var fastboot = new FastBoot({
distPath: fixture('html-entrypoint'),
});

return fastboot
.visit('/')
.then(r => r.html())
.then(html => {
expect(html).to.match(/Welcome to Ember/);
expect(html).to.not.match(/fastboot-script/);
});
});
});

0 comments on commit 9068d29

Please sign in to comment.