-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce html oriented manifest format (introduces better Embroider interop) #272
Conversation
src/ember-app.js
Outdated
if (manifest.htmlEntrypoint) { | ||
let htmlEntrypoint = require('./html-entrypoint'); | ||
({ appFiles, vendorFiles, html } = htmlEntrypoint(distPath, manifest.htmlEntrypoint)); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High level question: With this new API would addons be able to use updateFastBootManifest
hook + embroider?
If so, can we add a test for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand how that test would be relevant to this repo. fastboot
only consumes the manifest, it doesn't have anything to do with constructing the manifest.
(Embroider itself has test coverage to ensure that existing addons that use updateFastbootManifest work correctly, starting from https://github.com/embroider-build/embroider/blob/b88f660cc6a6629de916123318c909d5efd3d8a9/test-packages/fastboot-addon/index.js#L6)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that it only reads the config. I want to make sure the construct isn't broken. If it is in the embroider repo, I am fine with it.
src/ember-app.js
Outdated
let vendorFiles = manifest.vendorFiles.map(function(vendorFile) { | ||
return path.join(distPath, vendorFile); | ||
}); | ||
if (manifest.htmlEntrypoint) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is ember-cli-fastboot
writing this into the manifest? or is it embroider?
Asking because we have a concept of FastBootSchemaVersions
to ensure there is compatibility between the addon and this JS library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's written by embroider.
<fastboot-script src="assets/vendor.js"></fastboot-script> | ||
<fastboot-script src="assets/fastboot-test.js"></fastboot-script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does embroider add the script meta tags in the final built index.html? I don't see it as part of test fixtures. Am I missing anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is definitely beyond the scope of this repo, but if you are curious it happens in embroider/core here and here.
I'm not trying to be evasive here, I think it's important that we prioritize interface over implementation. If fastboot actually needed to care about where the script tags come from, then it would mean this is a bad interface because it leaks too much implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I misunderstood your question. Sorry. I was thinking about <fastboot-script>
, but you must have meant<meta name="your-app/config/environment">
.
I will make sure I show that as I incorporate all the feedback into this PR.
package.json
Outdated
@@ -30,6 +30,7 @@ | |||
"debug": "^4.1.1", | |||
"resolve": "^1.15.0", | |||
"simple-dom": "^1.4.0", | |||
"simple-html-tokenizer": "^0.5.9", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I think this is probably blocked on landing tildeio/simple-html-tokenizer#71
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only picked simple-html-tokenizer because that's what the bikeshed landed on in the previous PR.
IMO it would be better to use a more fully-featured off-the-shelf HTML parser and avoid issues like this. It only runs once at server boot, it's not like it's on any performance critical path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fine, but we either need to land that PR or use a different parser. As it stands, we cannot parse properly formatted HTML.
src/ember-app.js
Outdated
if (manifest.htmlEntrypoint) { | ||
let htmlEntrypoint = require('./html-entrypoint'); | ||
({ appFiles, vendorFiles, html } = htmlEntrypoint(distPath, manifest.htmlEntrypoint)); | ||
} else { | ||
debug('reading array of app file paths from manifest'); | ||
appFiles = manifest.appFiles.map(function(appFile) { | ||
return path.join(distPath, appFile); | ||
}); | ||
|
||
debug('reading array of vendor file paths from manifest'); | ||
vendorFiles = manifest.vendorFiles.map(function(vendorFile) { | ||
return path.join(distPath, vendorFile); | ||
}); | ||
|
||
htmlFile = path.join(distPath, manifest.htmlFile); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this should actually be incrementing the schema version (see here) as the opt-in and we "upgrade" to the latest version automatically. That reduces overall branching in this code, and means that fastboot
itself can always assume the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are ready to move all apps to this new format, then I agree and we can make this v5. I was not being so forward as to suggest that yet. At the moment it is an alternative, not an upgrade.
Making it an upgrade would certainly improve the error message people get if they try to use embroider without updating to this version of fastboot.
As for less branching: I realize that's the goal, but I don't actually agree that the current schema versioning system achieves it. Making this new format v5 would not result in fewer branches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are ready to move all apps to this new format, then I agree and we can make this v5. I was not being so forward as to suggest that yet. At the moment it is an alternative, not an upgrade.
I see no reason to keep two long running branches here, there should be one logical successor with the other path being planned for deprecation/removal.
AFAICT, there really isn't a reason that ember-cli-fastboot can't emit htmlEntrypoint
instead of its current system reducing yet another difference between Embroider and classic build worlds...
As for less branching: I realize that's the goal, but I don't actually agree that the current schema versioning system achieves it.
Ya, not sure I agree, but that isn't really the point. The goal of the system is to ensure we only deal with grabbing information from the manifest in a single place and we know what kind of thing we are dealing with. As it stands, you could easily have an htmlManifest
and vendorFiles
and have no clue why things don't work as you expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 if we think we have settled on htmlEntrypoint
being the API, it would be ideal for ember-cli-fastboot
to adopt it as well and reduce the clutter of maintaining between the two build systems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be ideal for ember-cli-fastboot to adopt it as well and reduce the clutter of maintaining between the two build systems
Ya, agree. Once we land this, we should make a tracking issue over on ember-cli-fastboot.
src/html-entrypoint.js
Outdated
for (let token of tokenize(html)) { | ||
if (token.type === 'StartTag' && ['script', 'fastboot-script'].includes(token.tagName)) { | ||
for (let [name, value] of token.attributes) { | ||
if (name === 'src') { | ||
appFiles.push(path.join(distPath, value)); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, @stefanpenner wrote https://github.com/stefanpenner/find-scripts-srcs-in-document specifically for this use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but that library doesn't actually meet the requirements here (we need to control the set of tag names it matches), and we're talking about an 18 line function that is used by exactly zero other packages.
Reasonable people can disagree on the "lots of tiny libraries" approach. For me, this falls far, far below the bar of deserving to be its own library. It's hyper-specialized for this one use case. People doing anything more general will reach for a complete parser instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The library was only created so that we could add proper tests for various combinations (and really just started as a one off script that we wrote together inline with some tests to confirm things worked). Seems 100% fine to have this logic inlined, but we need to beef up the test coverage for the parsing bit.
src/html-entrypoint.js
Outdated
if (token.type === 'StartTag' && ['script', 'fastboot-script'].includes(token.tagName)) { | ||
for (let [name, value] of token.attributes) { | ||
if (name === 'src') { | ||
appFiles.push(path.join(distPath, value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the testing within our application this should skip external scripts like
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" defer=""></script>
which results in FastBoot error
Error: ENOENT: no such file or directory, open '/var/folders/4z/h7wb5mp50sxbqpt8bdt_nlg40000gn/T/broccoli-60630hW7MihRllKHG/out-666-packager_runner_embroider_webpack/https:/cdn.onesignal.com/sdks/OneSignalSDK.js'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point, we already have this policy in embroider itself when it consumes the same HTML. We should only extract relative URLs, and we should probably keep the earlier suggestion of a data-fastboot-ignore attribute.
This implements a new v5 schema for fastboot. Unlike the previous json-oriented formats, this is an html-oriented format. HTML is better understood by general purpose build tools, which makes it easier to integrate with embroider. It also allows us to eliminate some redundancies -- for example, the app config no longer needs to exist in both package.json and the meta tags, since the HTML oriented format can use it directly from the meta tags. In package.json, the v5 schema supports: - `fastboot.schemaVersion: 5` - `fastboot.moduleWhitelist: []`: unchanged meaning from v4. - `fastboot.htmlEntrypoint: 'index.html'`: the path to your HTML entrypoint file - `name`: note that in the new format, we take the appName directly from the package.json top level. This field is used for nothing else by the time we get to fastboot, so there was no reason for redundancy. The HTML entrypoint file is any valid HTML, plus these fastboot-specific extensions: - `<fastboot-script>` tags work like `<script>` tags, but they will only run in fastboot and be stripped out before serving to browsers. - `<script data-fastboot-ignore></script>` will *not* run in fastboot, and the data attribute will be stripped out before serving to browsers. - `<script src="https://some-cdn.com/app.js" data-fastboot-src="assets/app.js"></script>` tells fastboot the local path to an asset that may have a different public URL for browser consumption. The data-fastboot-src attribute is stripped before serving to browsers. New html-oriented manifest format for embroider implementing data-fastboot-ignore and data-fastboot-src making htmlEntrypoint the new v5 schema And refactoring so we truly hide the differences in schema versions from the rest of the program. schema v5 This makes the htmlEntrypoint format be the official v5 schema.
I pushed a new (squashed) commit that incorporates the feedback to become the true v5 schema. This created a couple opportunities for simplification. Here is a summary of the complete new format: In package.json, the v5 schema supports:
All other fields that used to be supported in v4 under The HTML entrypoint file is any valid HTML, plus these fastboot-specific extensions:
|
We found one issue while testing this against some apps -- we need to account for the configured rootURL. I will add another commit that covers this case. |
Curious to understand who is responsible for stripping out |
@kratiahuja maybe the idea is that it just doesn't matter? It would show up as an HTML element (e.g. you could see it in the DOM explorer) but it would have no effect (wouldn't load the asset, wouldn't show any visible content). 🤔 |
|
Unless you mean, "when not using fastboot", in which case, yes, it's harmless if it stays in, but the assumption is that if you're using fastboot, your HTML is being served by fastboot. |
yes not in the case of not using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall!
if (schemaVersion < FastBootSchemaVersions.htmlEntrypoint) { | ||
({ appName, config, html, scripts } = loadManifest(distPath, pkg.fastboot, schemaVersion)); | ||
} else { | ||
appName = pkg.name; | ||
({ config, html, scripts } = htmlEntrypoint(appName, distPath, pkg.fastboot.htmlEntrypoint)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: It might be good to encapsulate this condition inside loadManifest
itself. From where we read it shouldn't matter for loading config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We no longer have a thing called "manifest" in the htmlEntrypoint case though, that's why I split it here.
if (name && name.endsWith('/config/environment')) { | ||
let content = JSON.parse(decodeURIComponent(element.getAttribute('content'))); | ||
content.APP = Object.assign({ autoboot: false }, content.APP); | ||
config[name.slice(0, -1 * '/config/environment'.length)] = content; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to avoid this? Seems a bit hacky IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you prefer name.split('/')[0]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that is a lot clearer :) Thanks!
@ef4 This lgtm overall, feel free to merge it (or let me know if you would like me to) . |
@kratiahuja yes could you merge and release? |
@ef4 / @thoov does the new html oriented manifest support https://github.com/ember-fastboot/ember-cli-fastboot/tree/e4d0b7c7bcdf82def0dc8726835b49d707673f41#providing-additional-config? I can't find where embroider writes fastboot specific config into the meta tag |
I think the fastboot-specific config still lives directly in the fastboot manifest, but I haven't looked at this lately. |
This PR introduces a new
htmlEntrypoint
property on the manifest which allows this entry point to be the source of truth finding assets instead of them being defined as apart of the manifest itself. The primary use case for this is to allow embroider's asset chunking to more naturally work. This is a simplification from an earlier attempt (ember-fastboot/ember-cli-fastboot#753) which can be closed after this is merged.This also introduces a concept of
<fastboot-script src=""></fastboot-script>
which is used to ignore scripts not intended for the browser. Here is how embroider uses these scripts:embroider usage 1 / embroider usage 2
These changes are opt-in and backwards compatible.
cc: @rwjblue / @kratiahuja
This PR is co-authored by @ef4