Skip to content
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

PRO-6775: External frontend support, docs cleanup #4799

Merged
merged 7 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
* Extra bundle detection when using external build module works properly now.
* Widget players are now properly invoked when they arrive later in the page load process.
* Fix permission grid tooltip display.
* Fixes a bug that crashes external frontend applications.
* Fixes a false positive warning for module not in use for project level submodules (e.g. `widges/module.js`) and dot-folders (e.g. `.DS_Store`).

### Adds

* It's possible now to target the HMR build when registering via `template.append` and `template.prepend`. Use `when: 'hmr:public'` or `when: 'hmr:apos'` that will be evaluated against the current asset `options.hmr` configuration.
* Adds asset module option `options.modulePreloadPolyfill` (default `true`) to allow disabling the polyfill preload for e.g. external front-ends.
* Adds `bundleMarkup` to the data sent to the external front-end, containing all markup for injecting Apostrophe UI in the front-end.

## 4.9.0 (2024-10-31)

Expand Down
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -699,11 +699,18 @@ async function apostrophe(options, telemetry, rootSpan) {
}
}
async function testDir(name) {
if (name.startsWith('.')) {
return;
}
// Projects that have different theme modules activated at different times
// are a frequent source of false positives for this warning, so ignore
// seemingly unused modules with "theme" in the name
if (!validSteps.includes(name)) {
try {
// It's a project level modules definition, skip it.
if (fs.existsSync(path.resolve(self.localModules, name, 'modules.js'))) {
return;
}
const submodule = await self.root.import(path.resolve(self.localModules, name, 'index.js'));
if (submodule && submodule.options && submodule.options.ignoreUnusedFolderWarning) {
return;
Expand Down
10 changes: 8 additions & 2 deletions modules/@apostrophecms/asset/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ module.exports = {
// Force the HMR WS port when it operates on the same process as Apostrophe.
// Most of the time you won't need to change this.
hmrPort: null,
// Let the external build module inject a pollyfill for the module preload,
// adding the `modulepreload` support for the browsers that don't support it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What browsers don't support it? Curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's widely supported now. But I'm just following the Vite standards and defaults: https://vite.dev/config/build-options.html#build-polyfillmodulepreload

// Can be disabled in e.g. external front-ends.
modulePreloadPolyfill: true,
// Completely disable the asset runtime auto-build system.
// When an external build module is registered, only manifest data
// will be loaded and no build will be executed.
Expand Down Expand Up @@ -477,6 +481,7 @@ module.exports = {
// - `devServer`: if `false`, the dev server is disabled. Otherwise, it's a string
// (enum) `public` or `apos`. Note that if `hmr` is disabled, the dev server will be always
// `false`.
// - `modulePreloadPolyfill`: if `true`, a module preload polyfill is injected.
// - `types`: optional array, if present it represents the only entrypoint types (entrypoint.type)
// that should be built.
// - `sourcemaps`: if `true`, the source maps are generated in production.
Expand All @@ -494,15 +499,16 @@ module.exports = {
isTask: !argv['check-apos-build'],
hmr: self.hasHMR(),
hmrPort: self.options.hmrPort,
modulePreloadPolyfill: self.options.modulePreloadPolyfill,
sourcemaps: self.options.productionSourceMaps
};
options.devServer = !options.isTask && self.hasDevServer()
? self.options.hmr
: false;

// Skip all public and keep only the apos scenes.
// Skip prebundled UI and keep only the apos scenes.
if (!self.options.publicBundle) {
options.types = [ 'apos', 'bundled' ];
options.types = [ 'apos', 'index' ];
}

return options;
Expand Down
12 changes: 6 additions & 6 deletions modules/@apostrophecms/asset/lib/build/external-module-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,8 @@ module.exports = (self) => {
// Returns an array of objects with the following properties:
// - `name`: the entrypoint name. It's usually the relative to `ui` folder
// name(`src`, `apos`, `public`) or an extra bundle name.
// - `label`: the human-readable label for the entrypoint, used to print CLI messages.
// - `type`: (enum) the entrypoint type. It can be `index`, `apos`, `custom` (e.g. extra bundles) or
// `bundled` (e.g. `ui/public`). Every type has associated manager that provides handling for the entrypoint.
// - `useMeta`: if `true`, the entrypoint will be created based on the source metadata (see
// `computeSourceMeta()` method).
// - `bundle`: if `true`, the entrypoint should be bundled by the build module.
// - `index`: if `true`, the entrypoint processes only `{name}/index.{js,scss}` module files.
// - `apos`: if `true`, the entrypoint processes components, icons and apps.
// - `ignoreSources`: an array of sources that shouldn't be processed when creating the entrypoint.
// - `sources`: an object with `js` and `scss` arrays of extra sources to be included in the entrypoint.
// These sources are not affected by the `ignoreSources` configuration.
Expand All @@ -84,10 +79,15 @@ module.exports = (self) => {
// - `prologue`: a string with the prologue to be added to the entrypoint.
// - `condition`: the JS `module` or `nomodule` condition. Undefined for no specific condition.
// - `outputs`: an array of output extensions for the entrypoint (currently not fully utilized)
// - `inputs`: an array of input extensions for the entrypoint (currently not fully utilized)
// - `scenes`: an array of scenes to be in the final post-bundle step. The scenes are instructions
// for the Apostrophe core to combine the builds and release them. Currently supported scenes are
// `apos` and `public` and custom scene names equal to extra bundle (only those who should be
// loaded separately in the browser).
//
// Additonal properties added after entrypoints are processed by the core and the external build module:
// - `manifest`: object, see the manifest section of `configureBuildModule()` docs for more information.
// - `bundles`: a `Set` containing the bundle names that this entrypoint is part of (both css and js).
getBuildEntrypoints(types, recompute = false) {
if (!self.hasBuildModule()) {
return self.builds;
Expand Down
2 changes: 1 addition & 1 deletion modules/@apostrophecms/asset/lib/build/internals.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ module.exports = (self) => {
enhancedConfig.ignoreSources.push(...bundleConfig.scss);
}
// 2.3. Add the extra bundle configuration so that
// it only processes the configured `sources` (`useMeta: false`).
// it only processes the configured `sources`
if (!bundleConfig.main) {
entrypoints.push({
name: bundleName,
Expand Down
4 changes: 3 additions & 1 deletion modules/@apostrophecms/schema/lib/addFieldTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,9 @@ module.exports = (self) => {

self.addFieldType({
name: 'object',
async convert(req, field, data, destination, { fetchRelationships = true, ancestors = {}, doc = {} } = {}) {
async convert(req, field, data, destination, {
fetchRelationships = true, ancestors = {}, doc = {}
} = {}) {
data = data[field.name];
const schema = field.schema;
const errors = [];
Expand Down
18 changes: 18 additions & 0 deletions modules/@apostrophecms/template/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,24 @@ module.exports = {
data.template = template;
// For simple cases (not piece pages and the like)
data.module = moduleName;

// Provide the `apos` scene bundles to the exsternal front-end
if (self.apos.asset.hasBuildModule()) {
const modulePreload = new Set();
data.bundleMarkup = {
js: self.apos.asset.getBundlePageMarkup({
scene: 'apos',
output: 'js',
modulePreload
}),
css: self.apos.asset.getBundlePageMarkup({
scene: 'apos',
output: 'css'
})
};
data.bundleMarkup.js.push(...Array.from(modulePreload));
}

return data;
},

Expand Down
Loading