Skip to content

Commit a4b0cc3

Browse files
authored
PRO-6775: External frontend support, bugfix, debug mode (#10)
1 parent be412d9 commit a4b0cc3

File tree

3 files changed

+104
-17
lines changed

3 files changed

+104
-17
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
* Adds postcss supports for the new `postcss-viewport-to-container-toggle` that allows the breakpoint preview feature to work.
88
* Loads postcss config file from project only for public builds.
99
* Adds `autoprefixer` plugin only for apos builds.
10+
* Adds module debug logs when in asset debug mode (`APOS_ASSET_DEBUG=1`).
11+
* Adds an option for disabling the module preload polyfill.
12+
* Adds support for `synthetic` entrypoints, that will only process the entrypoint `prologue`.
13+
14+
### Fixes
15+
16+
* Don't crash when there is no entrypoint of type `index`.
1017

1118
## 1.0.0-beta.1 (2024-10-31)
1219

index.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ module.exports = {
9696
// see @apostrophecms/assset:getBuildOptions() for the options shape.
9797
// A required interface for the asset module.
9898
async build(options = {}) {
99+
self.printDebug('build-options', { buildOptions: options });
99100
self.buildOptions = options;
100101
await self.buildBefore(options);
101102

@@ -115,6 +116,7 @@ module.exports = {
115116
},
116117
// A required interface for the asset module.
117118
async startDevServer(options) {
119+
self.printDebug('dev-server-build-options', { buildOptions: options });
118120
self.buildOptions = options;
119121
self.shouldCreateDevServer = true;
120122
await self.buildBefore(options);
@@ -124,7 +126,7 @@ module.exports = {
124126
build: currentBuild
125127
} = self.getCurrentMode(options.devServer);
126128

127-
self.entrypointsManifest.unshift(self.getViteClientEntrypoint(currentScenes));
129+
self.ensureViteClientEntry(self.entrypointsManifest, currentScenes, options);
128130

129131
let ts;
130132
if (currentBuild === 'public') {
@@ -151,9 +153,11 @@ module.exports = {
151153
},
152154
// A required interface for the asset module.
153155
// Initialize the watcher for triggering vite HMR via file
154-
// copy to the build source.
156+
// copy to the build source. This method is called always
157+
// after the `startDevServer` method.
155158
// `chokidar` is a chockidar `FSWatcher` or compatible instance.
156159
async watch(chokidar, buildOptions) {
160+
self.printDebug('watch-build-options', { buildOptions });
157161
self.buildWatchIndex();
158162
// Initialize our voting system to detect what entrypoints
159163
// are concerned with a given source file change.
@@ -175,12 +179,15 @@ module.exports = {
175179
// The options are same as the ones provided in the `build` and
176180
// `startDevServer` methods.
177181
async entrypoints(options) {
182+
self.printDebug('entrypoints-build-options', { buildOptions: options });
178183
const entrypoints = self.apos.asset.getBuildEntrypoints(options.types)
179184
.filter(entrypoint => entrypoint.condition !== 'nomodule');
180185

186+
self.ensureInitEntry(entrypoints);
187+
181188
if (options.devServer) {
182189
const { scenes } = self.getCurrentMode(options.devServer);
183-
entrypoints.unshift(self.getViteClientEntrypoint(scenes));
190+
self.ensureViteClientEntry(entrypoints, scenes, options);
184191
}
185192

186193
return entrypoints;

lib/internals.js

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const vitePostcssConfig = require('./vite-postcss-config');
1111
module.exports = (self) => {
1212
return {
1313
async initWhenReady() {
14+
self.isDebug = self.apos.asset.isDebugMode();
1415
self.buildRoot = self.apos.asset.getBuildRootDir();
1516
self.buildRootSource = path.join(self.buildRoot, self.buildSourceFolderName);
1617
self.distRoot = path.join(self.buildRoot, self.distFolderName);
@@ -41,6 +42,12 @@ module.exports = (self) => {
4142
await fs.mkdir(self.buildRootSource, { recursive: true });
4243
},
4344

45+
printDebug(id, ...args) {
46+
if (self.isDebug) {
47+
self.logDebug('vite-' + id, ...args);
48+
}
49+
},
50+
4451
async buildBefore(options = {}) {
4552
if (options.isTask) {
4653
await self.reset();
@@ -49,7 +56,8 @@ module.exports = (self) => {
4956
copyFiles: true
5057
});
5158
const entrypoints = self.apos.asset.getBuildEntrypoints(options.types);
52-
self.applyModulePreloadPolyfill(entrypoints);
59+
self.ensureInitEntry(entrypoints);
60+
self.applyModulePreloadPolyfill(entrypoints, options);
5361
await self.createImports(entrypoints);
5462

5563
// Copy the public files so that Vite is not complaining about missing files
@@ -77,6 +85,8 @@ module.exports = (self) => {
7785
await build(config);
7886
self.printLabels('apos', false);
7987

88+
self.printDebug('build-apos', { viteConfig: config });
89+
8090
return Date.now();
8191
},
8292

@@ -96,6 +106,8 @@ module.exports = (self) => {
96106
const { build, config } = await self.getViteBuild('public', options);
97107
await build(config);
98108
self.printLabels('public', false);
109+
110+
self.printDebug('build-public', { viteConfig: config });
99111
},
100112

101113
// Create an entrypoint configuration for the vite client.
@@ -144,13 +156,6 @@ module.exports = (self) => {
144156
if (options.isTask || process.env.APOS_DEV === '1') {
145157
return true;
146158
}
147-
// Forced build by type. Keeping the core current logic.
148-
// In play when asset option `publicBundle: false` is set (forces ony apos build).
149-
// Here we are only interested if we should force a "rebuild" or we can
150-
// use the cache.
151-
if (options.types?.includes(id)) {
152-
return true;
153-
}
154159
if (!self.hasViteBuildManifest(id)) {
155160
return true;
156161
}
@@ -163,6 +168,13 @@ module.exports = (self) => {
163168
return false;
164169
}
165170

171+
// Forced build by type. Keeping the core current logic.
172+
// In play when asset option `publicBundle: false` is set - forces apos build
173+
// if not cached.
174+
if (options.types?.includes(id)) {
175+
return true;
176+
}
177+
166178
return true;
167179
},
168180

@@ -445,6 +457,8 @@ module.exports = (self) => {
445457
self.apos.util.log(
446458
`HMR for "${options.devServer}" started`
447459
);
460+
461+
self.printDebug('dev-middleware', { viteConfig });
448462
},
449463

450464
// Compute metadata for the source files of all modules using
@@ -530,10 +544,19 @@ module.exports = (self) => {
530544

531545
async getEntrypointOutput(entrypoint, suppressErrors = false) {
532546
const manager = self.apos.asset.getEntrypointManger(entrypoint);
533-
const files = manager.getSourceFiles(
534-
self.currentSourceMeta,
535-
{ composePath: self.composeSourceImportPath }
536-
);
547+
548+
// synthetic entrypoints are not processed, they only provide
549+
// a way to inject additional code (prologue) into the build.
550+
const files = entrypoint.synthetic
551+
? entrypoint.outputs?.reduce((acc, ext) => ({
552+
...acc,
553+
[ext]: []
554+
}), {})
555+
: manager.getSourceFiles(
556+
self.currentSourceMeta,
557+
{ composePath: self.composeSourceImportPath }
558+
);
559+
537560
const output = await manager.getOutput(files, {
538561
modules: self.apos.asset.getRegisteredModules(),
539562
suppressErrors
@@ -543,13 +566,63 @@ module.exports = (self) => {
543566
return output;
544567
},
545568

569+
// Esnure there is always an `index` entrypoint, that holds the
570+
// prologue and the scenes, required for the polyfill.
571+
// The created synthetic entrypoint will only include the prologue.
572+
ensureInitEntry(entrypoints) {
573+
const exists = entrypoints.some((entry) => entry.type === 'index');
574+
if (exists) {
575+
return entrypoints;
576+
}
577+
const first = self.apos.asset.getBuildEntrypoints()
578+
.find((entry) => entry.type === 'index');
579+
580+
const index = {
581+
name: 'synth-src',
582+
type: 'index',
583+
// Synthetic entrypoints are not built, they only provide
584+
// a way to inject additional code (prologue) into the build.
585+
synthetic: true,
586+
label: first.label,
587+
scenes: first.scenes,
588+
inputs: [],
589+
outputs: [ 'js' ],
590+
condition: first.condition,
591+
prologue: first.prologue,
592+
ignoreSources: [],
593+
sources: {
594+
js: [],
595+
scss: []
596+
}
597+
};
598+
entrypoints.unshift(index);
599+
600+
return entrypoints;
601+
},
602+
603+
// Ensure Vite client is injected as a first entrypoint.
604+
// This should be called after the `ensureInitEntry` method,
605+
// basically as a last step. The method will add the Vite client
606+
// entrypoint only if needed.
607+
ensureViteClientEntry(entrypoints, scenes, buildOptions) {
608+
if (buildOptions.devServer && !entrypoints.some((entry) => entry.name === 'vite')) {
609+
entrypoints.unshift(self.getViteClientEntrypoint(scenes));
610+
}
611+
},
612+
546613
// Add vite module preload polyfill to the first `index` entrypoint.
547614
// We can probably remove it soon as the browser support looks good:
548615
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/modulepreload#browser_compatibility
549-
applyModulePreloadPolyfill(entrypoints) {
616+
//
617+
// The polyfill will be skipped for external frontends. External frontends
618+
// are responsible for including the polyfill themselves if needed.
619+
applyModulePreloadPolyfill(entrypoints, buildOptions) {
620+
if (!buildOptions.modulePreloadPolyfill) {
621+
return;
622+
}
550623
const first = entrypoints.find((entry) => entry.type === 'index');
551624
first.prologue = (first.prologue || '') +
552-
'\nimport \'vite/modulepreload-polyfill\';';
625+
'\nimport \'vite/modulepreload-polyfill\';';
553626
},
554627

555628
// Adds `manifest` property (object) to the entrypoint after a build.

0 commit comments

Comments
 (0)